From ce80b893312cb58bc5d83482179f6dfaf1708c82 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Mon, 23 Mar 2026 13:12:37 +0100 Subject: [PATCH 1/7] Split RegularTypeSchema into ResourceTypeSchema, ComplexTypeTypeSchema, LogicalTypeSchema - Export LogicalIdentifier, define three typed variants sharing RegularTypeSchemaBody - Redefine RegularTypeSchema as union alias of the three variants - Narrow type guard return types (isResourceTypeSchema, isComplexTypeTypeSchema, isLogicalTypeSchema) - Narrow collect functions in TypeSchemaIndex (collectResources, collectComplexTypes, collectLogicalModels) - Make groupByPackages generic to preserve narrowed types - Type nestedIndex as NestedType instead of TypeSchema - Use isSpecializationTypeSchema guard in TS writer instead of string array check --- docs/guides/typeschema-index.md | 6 +-- src/api/mustache/generator/NameGenerator.ts | 4 +- .../mustache/generator/ViewModelFactory.ts | 12 ++--- src/api/mustache/types.ts | 4 +- src/api/writer-generator/csharp/csharp.ts | 11 ++-- src/api/writer-generator/python.ts | 9 ++-- src/api/writer-generator/typescript/writer.ts | 5 +- src/typeschema/core/nested-types.ts | 10 ++-- src/typeschema/core/transformer.ts | 13 +++-- src/typeschema/ir/logic-promotion.ts | 4 +- src/typeschema/ir/tree-shake.ts | 8 +-- src/typeschema/types.ts | 52 +++++++++++-------- src/typeschema/utils.ts | 21 +++++--- test/unit/typeschema/utils.test.ts | 15 +++--- 14 files changed, 95 insertions(+), 79 deletions(-) diff --git a/docs/guides/typeschema-index.md b/docs/guides/typeschema-index.md index eb604f727..f6972999d 100644 --- a/docs/guides/typeschema-index.md +++ b/docs/guides/typeschema-index.md @@ -48,13 +48,13 @@ Why TypeSchemaIndex Matters Query schemas by type category: ```typescript -collectComplexTypes(): SpecializationTypeSchema[] +collectComplexTypes(): ComplexTypeTypeSchema[] Returns all complex types (datatypes, backbone elements) -collectResources(): SpecializationTypeSchema[] +collectResources(): ResourceTypeSchema[] Returns all FHIR resources (Patient, Observation, etc.) -collectLogicalModels(): SpecializationTypeSchema[] +collectLogicalModels(): LogicalTypeSchema[] Returns all logical models collectProfiles(): ProfileTypeSchema[] diff --git a/src/api/mustache/generator/NameGenerator.ts b/src/api/mustache/generator/NameGenerator.ts index e4e12ca71..59a1a70fd 100644 --- a/src/api/mustache/generator/NameGenerator.ts +++ b/src/api/mustache/generator/NameGenerator.ts @@ -1,4 +1,4 @@ -import type { Field, NestedTypeSchema, TypeIdentifier, TypeSchema } from "@typeschema/types"; +import type { Field, NestedType, TypeIdentifier, TypeSchema } from "@typeschema/types"; export type NameTransformation = { pattern: RegExp | string; @@ -86,7 +86,7 @@ export class NameGenerator { return this._generateTypeFromTypeRef((schema as any).type); } - public generateType(schemaOrRefOrString: TypeSchema | NestedTypeSchema | TypeIdentifier | string): string { + public generateType(schemaOrRefOrString: TypeSchema | NestedType | TypeIdentifier | string): string { if (typeof schemaOrRefOrString === "string") { return this._generateTypeName(schemaOrRefOrString); } diff --git a/src/api/mustache/generator/ViewModelFactory.ts b/src/api/mustache/generator/ViewModelFactory.ts index 78a7b6e09..e53f926ea 100644 --- a/src/api/mustache/generator/ViewModelFactory.ts +++ b/src/api/mustache/generator/ViewModelFactory.ts @@ -18,7 +18,7 @@ import { isComplexTypeIdentifier, isNotChoiceDeclarationField, isResourceIdentifier, - type NestedTypeSchema, + type NestedType, type RegularField, type TypeIdentifier, type TypeSchema, @@ -141,7 +141,7 @@ export class ViewModelFactory { return []; } - private _createParentsFor(base: TypeSchema | NestedTypeSchema, cache: ViewModelCache) { + private _createParentsFor(base: TypeSchema | NestedType, cache: ViewModelCache) { const parents: TypeViewModel[] = []; let parentRef: TypeIdentifier | undefined = "base" in base ? base.base : undefined; while (parentRef) { @@ -153,7 +153,7 @@ export class ViewModelFactory { } private _createForNestedType( - nested: NestedTypeSchema, + nested: NestedType, cache: ViewModelCache, nestedIn?: TypeSchema, ): ResolvedTypeViewModel { @@ -175,7 +175,7 @@ export class ViewModelFactory { } private _createTypeViewModel( - schema: TypeSchema | NestedTypeSchema, + schema: TypeSchema | NestedType, cache: ViewModelCache, nestedIn?: TypeSchema, ): TypeViewModel { @@ -233,7 +233,7 @@ export class ViewModelFactory { }; } - private _collectDependencies(schema: TypeSchema | NestedTypeSchema): TypeViewModel["dependencies"] { + private _collectDependencies(schema: TypeSchema | NestedType): TypeViewModel["dependencies"] { const dependencies: TypeViewModel["dependencies"] = { resources: [], complexTypes: [], @@ -315,7 +315,7 @@ export class ViewModelFactory { } private _collectNestedComplex( - schema: TypeSchema | NestedTypeSchema, + schema: TypeSchema | NestedType, cache: ViewModelCache, ): ResolvedTypeViewModel[] { const nested: ResolvedTypeViewModel[] = []; diff --git a/src/api/mustache/types.ts b/src/api/mustache/types.ts index 75f764840..8342eac6c 100644 --- a/src/api/mustache/types.ts +++ b/src/api/mustache/types.ts @@ -1,5 +1,5 @@ import type { IsPrefixed } from "@root/utils/types"; -import type { Field, NestedTypeSchema, TypeSchema } from "@typeschema/types"; +import type { Field, NestedType, TypeSchema } from "@typeschema/types"; export type DebugMixin = { debug: string; @@ -131,7 +131,7 @@ export type RootViewModel = T & { }; export type TypeViewModel = NamedViewModel & { - schema: TypeSchema | NestedTypeSchema; + schema: TypeSchema | NestedType; fields: FieldViewModel[]; dependencies: { diff --git a/src/api/writer-generator/csharp/csharp.ts b/src/api/writer-generator/csharp/csharp.ts index 29bdf546f..441144307 100644 --- a/src/api/writer-generator/csharp/csharp.ts +++ b/src/api/writer-generator/csharp/csharp.ts @@ -8,6 +8,7 @@ import type { Field, RegularField, TypeIdentifier } from "@typeschema/types"; import { type ChoiceFieldInstance, isChoiceDeclarationField, + type NestedType, type SpecializationTypeSchema, } from "@typeschema/types.ts"; import type { TypeSchemaIndex } from "@typeschema/utils.ts"; @@ -53,12 +54,12 @@ const getFieldModifiers = (field: Field) => { return field.required ? ["required"] : []; }; -const formatClassName = (schema: SpecializationTypeSchema) => { +const formatClassName = (schema: SpecializationTypeSchema | NestedType) => { const name = prefixReservedTypeName(getResourceName(schema.identifier)); return uppercaseFirstLetter(name); }; -const formatBaseClass = (schema: SpecializationTypeSchema) => { +const formatBaseClass = (schema: SpecializationTypeSchema | NestedType) => { return schema.base ? `: ${schema.base.name}` : ""; }; @@ -138,7 +139,7 @@ export class CSharp extends Writer { this.generateHelperFile(); } - private generateType(schema: SpecializationTypeSchema, packageName: string): void { + private generateType(schema: SpecializationTypeSchema | NestedType, packageName: string): void { const className = formatClassName(schema); const baseClass = formatBaseClass(schema); @@ -151,7 +152,7 @@ export class CSharp extends Writer { this.line(); } - private generateFields(schema: SpecializationTypeSchema, packageName: string): void { + private generateFields(schema: SpecializationTypeSchema | NestedType, packageName: string): void { if (!schema.fields) return; const sortedFields = Object.entries(schema.fields).sort(([a], [b]) => a.localeCompare(b)); @@ -161,7 +162,7 @@ export class CSharp extends Writer { } } - private generateNestedTypes(schema: SpecializationTypeSchema, packageName: string): void { + private generateNestedTypes(schema: SpecializationTypeSchema | NestedType, packageName: string): void { if (!("nested" in schema) || !schema.nested) return; this.line(); diff --git a/src/api/writer-generator/python.ts b/src/api/writer-generator/python.ts index 3357af693..2f3f13327 100644 --- a/src/api/writer-generator/python.ts +++ b/src/api/writer-generator/python.ts @@ -9,6 +9,7 @@ import { type EnumDefinition, type Field, isResourceTypeSchema, + type NestedType, type SpecializationTypeSchema, type TypeIdentifier, } from "@typeschema/types.ts"; @@ -409,7 +410,7 @@ export class Python extends Writer { this.pyImportFrom(`${this.opts.rootPackageName}.fhirpy_base_model`, "FhirpyBaseModel"); } - private generateType(schema: SpecializationTypeSchema): void { + private generateType(schema: SpecializationTypeSchema | NestedType): void { const className = deriveResourceName(schema.identifier); const superClasses = this.getSuperClasses(schema); @@ -420,7 +421,7 @@ export class Python extends Writer { this.line(); } - private getSuperClasses(schema: SpecializationTypeSchema): string[] { + private getSuperClasses(schema: SpecializationTypeSchema | NestedType): string[] { const bases: string[] = []; if (schema.base) bases.push(schema.base.name); bases.push(...this.injectSuperClasses(schema.identifier.url)); @@ -428,7 +429,7 @@ export class Python extends Writer { return bases; } - private generateClassBody(schema: SpecializationTypeSchema): void { + private generateClassBody(schema: SpecializationTypeSchema | NestedType): void { this.generateModelConfig(); if (!schema.fields) { @@ -473,7 +474,7 @@ export class Python extends Writer { this.line(")"); } - private generateFields(schema: SpecializationTypeSchema, schemaName: string): void { + private generateFields(schema: SpecializationTypeSchema | NestedType, schemaName: string): void { const sortedFields = Object.entries(schema.fields ?? []).sort(([a], [b]) => a.localeCompare(b)); for (const [fieldName, field] of sortedFields) { diff --git a/src/api/writer-generator/typescript/writer.ts b/src/api/writer-generator/typescript/writer.ts index 655c55453..25cdae174 100644 --- a/src/api/writer-generator/typescript/writer.ts +++ b/src/api/writer-generator/typescript/writer.ts @@ -11,6 +11,7 @@ import { isProfileTypeSchema, isResourceTypeSchema, isSpecializationTypeSchema, + type NestedType, packageMeta, packageMetaToFhir, type SpecializationTypeSchema, @@ -198,7 +199,7 @@ export class TypeScript extends Writer { this.lineSM(`${extFieldName}?: ${typeExpr}`); } - generateType(tsIndex: TypeSchemaIndex, schema: SpecializationTypeSchema) { + generateType(tsIndex: TypeSchemaIndex, schema: SpecializationTypeSchema | NestedType) { let name: string; // Generic types: Reference, Coding, CodeableConcept const genericTypes = ["Reference", "Coding", "CodeableConcept"]; @@ -285,7 +286,7 @@ export class TypeScript extends Writer { }); } - withPrimitiveTypeExtension(schema: TypeSchema): boolean { + withPrimitiveTypeExtension(schema: TypeSchema | NestedType): boolean { if (!this.opts.primitiveTypeExtension) return false; if (!isSpecializationTypeSchema(schema)) return false; for (const field of Object.values(schema.fields ?? {})) { diff --git a/src/typeschema/core/nested-types.ts b/src/typeschema/core/nested-types.ts index e6bac1b64..880092c88 100644 --- a/src/typeschema/core/nested-types.ts +++ b/src/typeschema/core/nested-types.ts @@ -12,7 +12,7 @@ import type { Field, Name, NestedIdentifier, - NestedTypeSchema, + NestedType, RichFHIRSchema, TypeIdentifier, } from "../types"; @@ -157,7 +157,7 @@ export function mkNestedTypes( register: Register, fhirSchema: RichFHIRSchema, logger?: CodegenLog, -): NestedTypeSchema[] | undefined { +): NestedType[] | undefined { if (!fhirSchema.elements) return undefined; const nested = collectNestedElements(fhirSchema, [], fhirSchema.elements).filter(([path, element]) => { @@ -172,7 +172,7 @@ export function mkNestedTypes( return true; }); - const nestedTypes = [] as NestedTypeSchema[]; + const nestedTypes = [] as NestedType[]; for (const [path, element] of nested) { const identifier = mkNestedIdentifier(register, fhirSchema, path); @@ -195,7 +195,7 @@ export function mkNestedTypes( const fields = transformNestedElements(register, fhirSchema, path, element.elements ?? {}, logger); - const nestedType: NestedTypeSchema = { + const nestedType: NestedType = { identifier, base, fields, @@ -208,7 +208,7 @@ export function mkNestedTypes( return nestedTypes.length === 0 ? undefined : nestedTypes; } -export function extractNestedDependencies(nestedTypes: NestedTypeSchema[]): TypeIdentifier[] { +export function extractNestedDependencies(nestedTypes: NestedType[]): TypeIdentifier[] { const deps: TypeIdentifier[] = []; for (const nested of nestedTypes) { diff --git a/src/typeschema/core/transformer.ts b/src/typeschema/core/transformer.ts index 9c37fb43f..bf207969e 100644 --- a/src/typeschema/core/transformer.ts +++ b/src/typeschema/core/transformer.ts @@ -16,7 +16,7 @@ import { type Identifier, isNestedIdentifier, isProfileIdentifier, - type NestedTypeSchema, + type NestedType, type ProfileIdentifier, packageMetaToFhir, type RichFHIRSchema, @@ -99,7 +99,7 @@ export async function transformValueSet( const collectRawDeps = ( base: TypeIdentifier | undefined, fields: Record | undefined, - nestedTypes: NestedTypeSchema[] | undefined, + nestedTypes: NestedType[] | undefined, ): TypeIdentifier[] => { const deps: TypeIdentifier[] = []; if (base) deps.push(base); @@ -112,7 +112,7 @@ export const extractDependencies = ( identifier: Identifier, base: TypeIdentifier | undefined, fields: Record | undefined, - nestedTypes: NestedTypeSchema[] | undefined, + nestedTypes: NestedType[] | undefined, ): Identifier[] | undefined => { const deps = collectRawDeps(base, fields, nestedTypes); @@ -129,7 +129,7 @@ export const extractProfileDependencies = ( identifier: ProfileIdentifier, base: TypeIdentifier | undefined, fields: Record | undefined, - nestedTypes: NestedTypeSchema[] | undefined, + nestedTypes: NestedType[] | undefined, ): TypeIdentifier[] | undefined => { const deps = collectRawDeps(base, fields, nestedTypes); const filtered = deps.filter((dep) => dep.url !== identifier.url); @@ -176,7 +176,7 @@ export function transformFhirSchema(register: Register, fhirSchema: RichFHIRSche } else { assert(!isNestedIdentifier(identifier), `Unexpected nested identifier for ${fhirSchema.url}`); const rawDeps = extractDependencies(identifier, base, fields, nested); - const specialization: SpecializationTypeSchema = { + typeSchema = { identifier, base, fields, @@ -184,8 +184,7 @@ export function transformFhirSchema(register: Register, fhirSchema: RichFHIRSche description: fhirSchema.description, dependencies: rawDeps, typeFamily: undefined, // NOTE: should be populateTypeFamily later. - }; - typeSchema = specialization; + } as TypeSchema; } const bindingSchemas = collectBindingSchemas(register, fhirSchema, logger); diff --git a/src/typeschema/ir/logic-promotion.ts b/src/typeschema/ir/logic-promotion.ts index 033190bbf..f11f39419 100644 --- a/src/typeschema/ir/logic-promotion.ts +++ b/src/typeschema/ir/logic-promotion.ts @@ -8,7 +8,7 @@ import { isProfileTypeSchema, isSpecializationTypeSchema, isValueSetTypeSchema, - type NestedTypeSchema, + type NestedType, type PkgName, type TypeIdentifier, } from "@root/typeschema/types"; @@ -51,7 +51,7 @@ export const promoteLogical = (tsIndex: TypeSchemaIndex, promotes: LogicalPromot cloned.dependencies = cloned.dependencies?.map(replace); if (isSpecializationTypeSchema(cloned) || isProfileTypeSchema(cloned)) { cloned.fields = replaceInFields(cloned.fields); - cloned.nested = cloned.nested?.map((n: NestedTypeSchema) => { + cloned.nested = cloned.nested?.map((n: NestedType) => { return { ...n, base: replace(n.base), diff --git a/src/typeschema/ir/tree-shake.ts b/src/typeschema/ir/tree-shake.ts index b979045d9..9133f5889 100644 --- a/src/typeschema/ir/tree-shake.ts +++ b/src/typeschema/ir/tree-shake.ts @@ -192,12 +192,12 @@ export const treeShakeTypeSchema = (schema: TypeSchema, rule: TreeShakeRule, _lo if (rule.selectFields) { if (rule.ignoreFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule"); - mutableSelectFields(schema, rule.selectFields); + mutableSelectFields(schema as SpecializationTypeSchema, rule.selectFields); } if (rule.ignoreFields) { if (rule.selectFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule"); - mutableIgnoreFields(schema, rule.ignoreFields); + mutableIgnoreFields(schema as SpecializationTypeSchema, rule.ignoreFields); } if (isProfileTypeSchema(schema) && rule.ignoreExtensions) { @@ -220,7 +220,7 @@ export const treeShakeTypeSchema = (schema: TypeSchema, rule: TreeShakeRule, _lo } }); }; - collectUsedNestedTypes(schema); + collectUsedNestedTypes(schema as SpecializationTypeSchema); schema.nested = schema.nested.filter((n) => usedTypes.has(n.identifier.url)); } @@ -271,7 +271,7 @@ export const treeShake = (tsIndex: TypeSchemaIndex, treeShake: TreeShakeConf): T for (const nest of schema.nested) { if (isNestedIdentifier(nest.identifier)) continue; const id = JSON.stringify(nest.identifier); - if (!acc[id]) newSchemas.push(nest); + if (!acc[id]) newSchemas.push(nest as unknown as TypeSchema); } } } diff --git a/src/typeschema/types.ts b/src/typeschema/types.ts index 264fbd35d..15efb18d6 100644 --- a/src/typeschema/types.ts +++ b/src/typeschema/types.ts @@ -100,7 +100,7 @@ export type ValueSetIdentifier = { kind: "value-set" } & IdentifierBase; export type NestedIdentifier = { kind: "nested" } & IdentifierBase; export type BindingIdentifier = { kind: "binding" } & IdentifierBase; export type ProfileIdentifier = { kind: "profile" } & IdentifierBase; -type LogicalIdentifier = { kind: "logical" } & IdentifierBase; +export type LogicalIdentifier = { kind: "logical" } & IdentifierBase; export type Identifier = | PrimitiveIdentifier @@ -155,7 +155,9 @@ export type TypeSchema = | BindingTypeSchema | ProfileTypeSchema; -export const isSpecializationTypeSchema = (schema: TypeSchema | undefined): schema is SpecializationTypeSchema => { +type SchemaGuardInput = { identifier: TypeIdentifier } | undefined; + +export const isSpecializationTypeSchema = (schema: SchemaGuardInput): schema is SpecializationTypeSchema => { return ( schema?.identifier.kind === "resource" || schema?.identifier.kind === "complex-type" || @@ -163,33 +165,33 @@ export const isSpecializationTypeSchema = (schema: TypeSchema | undefined): sche ); }; -export const isComplexTypeTypeSchema = (schema: TypeSchema | undefined): schema is SpecializationTypeSchema => { +export const isComplexTypeTypeSchema = (schema: SchemaGuardInput): schema is ComplexTypeTypeSchema => { return schema?.identifier.kind === "complex-type"; }; -export const isResourceTypeSchema = (schema: TypeSchema | undefined): schema is SpecializationTypeSchema => { +export const isResourceTypeSchema = (schema: SchemaGuardInput): schema is ResourceTypeSchema => { return schema?.identifier.kind === "resource"; }; -export const isPrimitiveTypeSchema = (schema: TypeSchema | undefined): schema is PrimitiveTypeSchema => { +export const isPrimitiveTypeSchema = (schema: SchemaGuardInput): schema is PrimitiveTypeSchema => { return schema?.identifier.kind === "primitive-type"; }; -export const isLogicalTypeSchema = (schema: TypeSchema | undefined): schema is SpecializationTypeSchema => { +export const isLogicalTypeSchema = (schema: SchemaGuardInput): schema is LogicalTypeSchema => { return schema?.identifier.kind === "logical"; }; -export const isProfileTypeSchema = (schema: TypeSchema | undefined): schema is ProfileTypeSchema => { +export const isProfileTypeSchema = (schema: SchemaGuardInput): schema is ProfileTypeSchema => { return schema?.identifier.kind === "profile"; }; -export function isBindingSchema(schema: TypeSchema | undefined): schema is BindingTypeSchema { +export const isBindingSchema = (schema: SchemaGuardInput): schema is BindingTypeSchema => { return schema?.identifier.kind === "binding"; -} +}; -export function isValueSetTypeSchema(schema: TypeSchema | undefined): schema is ValueSetTypeSchema { +export const isValueSetTypeSchema = (schema: SchemaGuardInput): schema is ValueSetTypeSchema => { return schema?.identifier.kind === "value-set"; -} +}; interface PrimitiveTypeSchema { identifier: PrimitiveIdentifier; @@ -198,7 +200,7 @@ interface PrimitiveTypeSchema { dependencies?: TypeIdentifier[]; } -export interface NestedTypeSchema { +export interface NestedType { identifier: NestedIdentifier; base: TypeIdentifier; fields: Record; @@ -211,7 +213,7 @@ export interface ProfileTypeSchema { fields?: Record; extensions?: ProfileExtension[]; dependencies?: TypeIdentifier[]; - nested?: NestedTypeSchema[]; + nested?: NestedType[]; } export type DiscriminatorType = "value" | "exists" | "pattern" | "type" | "profile"; @@ -266,20 +268,26 @@ export const extractExtensionDeps = (ext: ProfileExtension): TypeIdentifier[] => ...(ext.subExtensions?.flatMap((sub) => (sub.valueFieldType ? [sub.valueFieldType] : [])) ?? []), ]; -export interface SpecializationTypeSchema { - // TODO: restrict to ResourceIdentifier | ComplexTypeIdentifier | LogicalIdentifier - identifier: TypeIdentifier; +type SpecializationTypeSchemaBody = { base?: TypeIdentifier; description?: string; fields?: { [k: string]: Field }; - nested?: NestedTypeSchema[]; + nested?: NestedType[]; dependencies?: Identifier[]; /** Transitive children grouped by kind (e.g. Resource → { resources: [DomainResource, Patient, …] }) */ - typeFamily?: { - resources?: ResourceIdentifier[]; - complexTypes?: ComplexTypeIdentifier[]; - }; -} + typeFamily?: TypeFamily; +}; + +export type TypeFamily = { + resources?: ResourceIdentifier[]; + complexTypes?: ComplexTypeIdentifier[]; +}; + +export type ResourceTypeSchema = { identifier: ResourceIdentifier } & SpecializationTypeSchemaBody; +export type ComplexTypeTypeSchema = { identifier: ComplexTypeIdentifier } & SpecializationTypeSchemaBody; +export type LogicalTypeSchema = { identifier: LogicalIdentifier } & SpecializationTypeSchemaBody; + +export type SpecializationTypeSchema = ResourceTypeSchema | ComplexTypeTypeSchema | LogicalTypeSchema; export interface RegularField { type: TypeIdentifier; diff --git a/src/typeschema/utils.ts b/src/typeschema/utils.ts index c8c49a4bd..595346a42 100644 --- a/src/typeschema/utils.ts +++ b/src/typeschema/utils.ts @@ -7,6 +7,7 @@ import type { Register } from "./register"; import { type CanonicalUrl, type ChoiceFieldInstance, + type ComplexTypeTypeSchema, type ConstrainedChoiceInfo, type Field, isChoiceDeclarationField, @@ -18,10 +19,14 @@ import { isResourceIdentifier, isResourceTypeSchema, isSpecializationTypeSchema, + type LogicalTypeSchema, + type NestedType, type PkgName, type ProfileExtension, type ProfileTypeSchema, + type ResourceTypeSchema, type SpecializationTypeSchema, + type TypeFamily, type TypeIdentifier, type TypeSchema, } from "./types"; @@ -135,7 +140,7 @@ const populateTypeFamily = (schemas: TypeSchema[]): void => { if (allChildren.length === 0) continue; const resources = allChildren.filter(isResourceIdentifier); const complexTypes = allChildren.filter(isComplexTypeIdentifier); - const family: NonNullable = {}; + const family: TypeFamily = {}; if (resources.length > 0) family.resources = resources; if (complexTypes.length > 0) family.complexTypes = complexTypes; if (Object.keys(family).length > 0) schema.typeFamily = family; @@ -150,9 +155,9 @@ export type TypeSchemaIndex = { schemas: TypeSchema[]; schemasByPackage: Record; register?: Register; - collectComplexTypes: () => SpecializationTypeSchema[]; - collectResources: () => SpecializationTypeSchema[]; - collectLogicalModels: () => SpecializationTypeSchema[]; + collectComplexTypes: () => ComplexTypeTypeSchema[]; + collectResources: () => ResourceTypeSchema[]; + collectLogicalModels: () => LogicalTypeSchema[]; collectProfiles: () => ProfileTypeSchema[]; resolve: (id: TypeIdentifier) => TypeSchema | undefined; resolveByUrl: (pkgName: PkgName, url: CanonicalUrl) => TypeSchema | undefined; @@ -188,7 +193,7 @@ export const mkTypeSchemaIndex = ( }, ): TypeSchemaIndex => { const index: Record> = {}; - const nestedIndex: Record> = {}; + const nestedIndex: Record> = {}; const append = (schema: TypeSchema) => { const url = schema.identifier.url; const pkg = schema.identifier.package; @@ -218,8 +223,8 @@ export const mkTypeSchemaIndex = ( } populateTypeFamily(schemas); - const resolve = (id: TypeIdentifier) => { - if (id.kind === "nested") return nestedIndex[id.url]?.[id.package]; + const resolve = (id: TypeIdentifier): TypeSchema | undefined => { + if (id.kind === "nested") return nestedIndex[id.url]?.[id.package] as unknown as TypeSchema | undefined; return index[id.url]?.[id.package]; }; const resolveByUrl = (pkgName: PkgName, url: CanonicalUrl) => { @@ -231,7 +236,7 @@ export const mkTypeSchemaIndex = ( } } if (index[url]?.[pkgName]) return index[url]?.[pkgName]; - if (nestedIndex[url]?.[pkgName]) return nestedIndex[url]?.[pkgName]; + if (nestedIndex[url]?.[pkgName]) return nestedIndex[url]?.[pkgName] as unknown as TypeSchema; logger?.dryWarn(`Type '${url}' not found in '${pkgName}'`); // Fallback: search across all packages when type exists elsewhere diff --git a/test/unit/typeschema/utils.test.ts b/test/unit/typeschema/utils.test.ts index ba1521399..9c53cff53 100644 --- a/test/unit/typeschema/utils.test.ts +++ b/test/unit/typeschema/utils.test.ts @@ -6,6 +6,7 @@ import type { RegularField, SpecializationTypeSchema, TypeIdentifier, + TypeSchema, } from "@typeschema/types"; import { mkTypeSchemaIndex } from "@typeschema/utils"; @@ -46,7 +47,7 @@ describe("TypeSchema Index", () => { }, }; - const bSchema: SpecializationTypeSchema = { + const bSchema: ProfileTypeSchema = { identifier: { name: "B" as Name, package: "test", @@ -80,7 +81,7 @@ describe("TypeSchema Index", () => { }); it("should handle a schema without a base reference", () => { - const bSchema: SpecializationTypeSchema = { + const bSchema: TypeSchema = { identifier: { name: "B" as Name, package: "test", @@ -89,7 +90,7 @@ describe("TypeSchema Index", () => { url: "http://example.org/StructureDefinition/B" as CanonicalUrl, }, // No base provided - }; + } as TypeSchema; const index = mkTypeSchemaIndex([bSchema], {}); const result = index.hierarchy(bSchema); @@ -108,7 +109,7 @@ describe("TypeSchema Index", () => { }, }; - const bSchema: SpecializationTypeSchema = { + const bSchema: ProfileTypeSchema = { identifier: { name: "B" as Name, package: "test", @@ -119,7 +120,7 @@ describe("TypeSchema Index", () => { base: aSchema.identifier, }; - const cSchema: SpecializationTypeSchema = { + const cSchema: ProfileTypeSchema = { identifier: { name: "C" as Name, package: "test", @@ -137,7 +138,7 @@ describe("TypeSchema Index", () => { }); it("should throw an error when base type cannot be resolved", () => { - const bSchema: SpecializationTypeSchema = { + const bSchema: ProfileTypeSchema = { identifier: { name: "B" as Name, package: "test", @@ -172,7 +173,7 @@ describe("TypeSchema Index", () => { }, }; - const bSchema: SpecializationTypeSchema = { + const bSchema: ProfileTypeSchema = { identifier: { name: "B" as Name, package: "TEST", From a3409c0aa27cb5280e24906b7a96f6c7d7bef545 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Mon, 23 Mar 2026 13:44:27 +0100 Subject: [PATCH 2/7] fix: Use type schema guards instead of identifier checks in ViewModelFactory --- CLAUDE.md | 1 + src/api/mustache/generator/NameGenerator.ts | 4 +- .../mustache/generator/ViewModelFactory.ts | 22 +++++----- src/api/mustache/types.ts | 4 +- src/api/writer-generator/csharp/csharp.ts | 12 +++--- src/api/writer-generator/python.ts | 10 ++--- src/api/writer-generator/typescript/writer.ts | 6 +-- src/typeschema/core/nested-types.ts | 10 ++--- src/typeschema/core/transformer.ts | 43 +++++++++++++------ src/typeschema/ir/logic-promotion.ts | 4 +- src/typeschema/ir/tree-shake.ts | 15 +++---- src/typeschema/types.ts | 22 +++++++--- src/typeschema/utils.ts | 4 +- .../__snapshots__/introspection.test.ts.snap | 16 +++---- .../__snapshots__/snapshot.test.ts.snap | 2 +- test/unit/typeschema/r4.test.ts | 4 +- test/unit/typeschema/transformer.test.ts | 1 + test/unit/typeschema/transformer/r4.test.ts | 4 +- test/unit/typeschema/utils.test.ts | 8 ++-- 19 files changed, 103 insertions(+), 89 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 8957acecc..5d370a241 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -123,6 +123,7 @@ FHIR Package → TypeSchema Generator → TypeSchema Format → Code Generators - In code generators (writer-generator): use `curlyBlock` and `squareBlock` helpers for writing structured output instead of manual indent/deindent or string concatenation - Use `Record` instead of `Map` unless there is a significant reason for `Map` (e.g. non-string keys, iteration order guarantees, frequent deletion) - Prefer single-line guard clauses without braces: `if (!x) throw new Error("...");` instead of wrapping in `{ }` +- Do not check `kind` of `Identifier`/`TypeIdentifier`/`TypeSchema` by manually comparing the `kind` field. Use dedicated predicates (`isPrimitiveIdentifier`, `isSpecializationTypeSchema`, etc.) ### Testing Strategy - Uses Bun's built-in test runner diff --git a/src/api/mustache/generator/NameGenerator.ts b/src/api/mustache/generator/NameGenerator.ts index 59a1a70fd..e4e12ca71 100644 --- a/src/api/mustache/generator/NameGenerator.ts +++ b/src/api/mustache/generator/NameGenerator.ts @@ -1,4 +1,4 @@ -import type { Field, NestedType, TypeIdentifier, TypeSchema } from "@typeschema/types"; +import type { Field, NestedTypeSchema, TypeIdentifier, TypeSchema } from "@typeschema/types"; export type NameTransformation = { pattern: RegExp | string; @@ -86,7 +86,7 @@ export class NameGenerator { return this._generateTypeFromTypeRef((schema as any).type); } - public generateType(schemaOrRefOrString: TypeSchema | NestedType | TypeIdentifier | string): string { + public generateType(schemaOrRefOrString: TypeSchema | NestedTypeSchema | TypeIdentifier | string): string { if (typeof schemaOrRefOrString === "string") { return this._generateTypeName(schemaOrRefOrString); } diff --git a/src/api/mustache/generator/ViewModelFactory.ts b/src/api/mustache/generator/ViewModelFactory.ts index e53f926ea..282368978 100644 --- a/src/api/mustache/generator/ViewModelFactory.ts +++ b/src/api/mustache/generator/ViewModelFactory.ts @@ -15,10 +15,10 @@ import type { IsPrefixed } from "@root/utils/types"; import { type ChoiceFieldInstance, type Field, - isComplexTypeIdentifier, + isComplexTypeTypeSchema, isNotChoiceDeclarationField, - isResourceIdentifier, - type NestedType, + isResourceTypeSchema, + type NestedTypeSchema, type RegularField, type TypeIdentifier, type TypeSchema, @@ -127,13 +127,13 @@ export class ViewModelFactory { private _createChildrenFor(typeRef: TypeIdentifier, cache: ViewModelCache, nestedIn?: TypeSchema): TypeViewModel[] { const schema = this.tsIndex.resolve(typeRef); - if (!schema || !("typeFamily" in schema)) return []; - if (isComplexTypeIdentifier(typeRef)) { + if (!schema) return []; + if (isComplexTypeTypeSchema(schema)) { return (schema.typeFamily?.complexTypes ?? []) .filter(this.filterPred) .map((childRef: TypeIdentifier) => this._createFor(childRef, cache, nestedIn)); } - if (isResourceIdentifier(typeRef)) { + if (isResourceTypeSchema(schema)) { return (schema.typeFamily?.resources ?? []) .filter(this.filterPred) .map((childRef: TypeIdentifier) => this._createFor(childRef, cache, nestedIn)); @@ -141,7 +141,7 @@ export class ViewModelFactory { return []; } - private _createParentsFor(base: TypeSchema | NestedType, cache: ViewModelCache) { + private _createParentsFor(base: TypeSchema | NestedTypeSchema, cache: ViewModelCache) { const parents: TypeViewModel[] = []; let parentRef: TypeIdentifier | undefined = "base" in base ? base.base : undefined; while (parentRef) { @@ -153,7 +153,7 @@ export class ViewModelFactory { } private _createForNestedType( - nested: NestedType, + nested: NestedTypeSchema, cache: ViewModelCache, nestedIn?: TypeSchema, ): ResolvedTypeViewModel { @@ -175,7 +175,7 @@ export class ViewModelFactory { } private _createTypeViewModel( - schema: TypeSchema | NestedType, + schema: TypeSchema | NestedTypeSchema, cache: ViewModelCache, nestedIn?: TypeSchema, ): TypeViewModel { @@ -233,7 +233,7 @@ export class ViewModelFactory { }; } - private _collectDependencies(schema: TypeSchema | NestedType): TypeViewModel["dependencies"] { + private _collectDependencies(schema: TypeSchema | NestedTypeSchema): TypeViewModel["dependencies"] { const dependencies: TypeViewModel["dependencies"] = { resources: [], complexTypes: [], @@ -315,7 +315,7 @@ export class ViewModelFactory { } private _collectNestedComplex( - schema: TypeSchema | NestedType, + schema: TypeSchema | NestedTypeSchema, cache: ViewModelCache, ): ResolvedTypeViewModel[] { const nested: ResolvedTypeViewModel[] = []; diff --git a/src/api/mustache/types.ts b/src/api/mustache/types.ts index 8342eac6c..75f764840 100644 --- a/src/api/mustache/types.ts +++ b/src/api/mustache/types.ts @@ -1,5 +1,5 @@ import type { IsPrefixed } from "@root/utils/types"; -import type { Field, NestedType, TypeSchema } from "@typeschema/types"; +import type { Field, NestedTypeSchema, TypeSchema } from "@typeschema/types"; export type DebugMixin = { debug: string; @@ -131,7 +131,7 @@ export type RootViewModel = T & { }; export type TypeViewModel = NamedViewModel & { - schema: TypeSchema | NestedType; + schema: TypeSchema | NestedTypeSchema; fields: FieldViewModel[]; dependencies: { diff --git a/src/api/writer-generator/csharp/csharp.ts b/src/api/writer-generator/csharp/csharp.ts index 441144307..61dc8ecfa 100644 --- a/src/api/writer-generator/csharp/csharp.ts +++ b/src/api/writer-generator/csharp/csharp.ts @@ -8,7 +8,7 @@ import type { Field, RegularField, TypeIdentifier } from "@typeschema/types"; import { type ChoiceFieldInstance, isChoiceDeclarationField, - type NestedType, + type NestedTypeSchema, type SpecializationTypeSchema, } from "@typeschema/types.ts"; import type { TypeSchemaIndex } from "@typeschema/utils.ts"; @@ -54,12 +54,12 @@ const getFieldModifiers = (field: Field) => { return field.required ? ["required"] : []; }; -const formatClassName = (schema: SpecializationTypeSchema | NestedType) => { +const formatClassName = (schema: SpecializationTypeSchema | NestedTypeSchema) => { const name = prefixReservedTypeName(getResourceName(schema.identifier)); return uppercaseFirstLetter(name); }; -const formatBaseClass = (schema: SpecializationTypeSchema | NestedType) => { +const formatBaseClass = (schema: SpecializationTypeSchema | NestedTypeSchema) => { return schema.base ? `: ${schema.base.name}` : ""; }; @@ -139,7 +139,7 @@ export class CSharp extends Writer { this.generateHelperFile(); } - private generateType(schema: SpecializationTypeSchema | NestedType, packageName: string): void { + private generateType(schema: SpecializationTypeSchema | NestedTypeSchema, packageName: string): void { const className = formatClassName(schema); const baseClass = formatBaseClass(schema); @@ -152,7 +152,7 @@ export class CSharp extends Writer { this.line(); } - private generateFields(schema: SpecializationTypeSchema | NestedType, packageName: string): void { + private generateFields(schema: SpecializationTypeSchema | NestedTypeSchema, packageName: string): void { if (!schema.fields) return; const sortedFields = Object.entries(schema.fields).sort(([a], [b]) => a.localeCompare(b)); @@ -162,7 +162,7 @@ export class CSharp extends Writer { } } - private generateNestedTypes(schema: SpecializationTypeSchema | NestedType, packageName: string): void { + private generateNestedTypes(schema: SpecializationTypeSchema | NestedTypeSchema, packageName: string): void { if (!("nested" in schema) || !schema.nested) return; this.line(); diff --git a/src/api/writer-generator/python.ts b/src/api/writer-generator/python.ts index 2f3f13327..40f69e54e 100644 --- a/src/api/writer-generator/python.ts +++ b/src/api/writer-generator/python.ts @@ -9,7 +9,7 @@ import { type EnumDefinition, type Field, isResourceTypeSchema, - type NestedType, + type NestedTypeSchema, type SpecializationTypeSchema, type TypeIdentifier, } from "@typeschema/types.ts"; @@ -410,7 +410,7 @@ export class Python extends Writer { this.pyImportFrom(`${this.opts.rootPackageName}.fhirpy_base_model`, "FhirpyBaseModel"); } - private generateType(schema: SpecializationTypeSchema | NestedType): void { + private generateType(schema: SpecializationTypeSchema | NestedTypeSchema): void { const className = deriveResourceName(schema.identifier); const superClasses = this.getSuperClasses(schema); @@ -421,7 +421,7 @@ export class Python extends Writer { this.line(); } - private getSuperClasses(schema: SpecializationTypeSchema | NestedType): string[] { + private getSuperClasses(schema: SpecializationTypeSchema | NestedTypeSchema): string[] { const bases: string[] = []; if (schema.base) bases.push(schema.base.name); bases.push(...this.injectSuperClasses(schema.identifier.url)); @@ -429,7 +429,7 @@ export class Python extends Writer { return bases; } - private generateClassBody(schema: SpecializationTypeSchema | NestedType): void { + private generateClassBody(schema: SpecializationTypeSchema | NestedTypeSchema): void { this.generateModelConfig(); if (!schema.fields) { @@ -474,7 +474,7 @@ export class Python extends Writer { this.line(")"); } - private generateFields(schema: SpecializationTypeSchema | NestedType, schemaName: string): void { + private generateFields(schema: SpecializationTypeSchema | NestedTypeSchema, schemaName: string): void { const sortedFields = Object.entries(schema.fields ?? []).sort(([a], [b]) => a.localeCompare(b)); for (const [fieldName, field] of sortedFields) { diff --git a/src/api/writer-generator/typescript/writer.ts b/src/api/writer-generator/typescript/writer.ts index 25cdae174..7208eb711 100644 --- a/src/api/writer-generator/typescript/writer.ts +++ b/src/api/writer-generator/typescript/writer.ts @@ -11,7 +11,7 @@ import { isProfileTypeSchema, isResourceTypeSchema, isSpecializationTypeSchema, - type NestedType, + type NestedTypeSchema, packageMeta, packageMetaToFhir, type SpecializationTypeSchema, @@ -199,7 +199,7 @@ export class TypeScript extends Writer { this.lineSM(`${extFieldName}?: ${typeExpr}`); } - generateType(tsIndex: TypeSchemaIndex, schema: SpecializationTypeSchema | NestedType) { + generateType(tsIndex: TypeSchemaIndex, schema: SpecializationTypeSchema | NestedTypeSchema) { let name: string; // Generic types: Reference, Coding, CodeableConcept const genericTypes = ["Reference", "Coding", "CodeableConcept"]; @@ -286,7 +286,7 @@ export class TypeScript extends Writer { }); } - withPrimitiveTypeExtension(schema: TypeSchema | NestedType): boolean { + withPrimitiveTypeExtension(schema: TypeSchema | NestedTypeSchema): boolean { if (!this.opts.primitiveTypeExtension) return false; if (!isSpecializationTypeSchema(schema)) return false; for (const field of Object.values(schema.fields ?? {})) { diff --git a/src/typeschema/core/nested-types.ts b/src/typeschema/core/nested-types.ts index 880092c88..e6bac1b64 100644 --- a/src/typeschema/core/nested-types.ts +++ b/src/typeschema/core/nested-types.ts @@ -12,7 +12,7 @@ import type { Field, Name, NestedIdentifier, - NestedType, + NestedTypeSchema, RichFHIRSchema, TypeIdentifier, } from "../types"; @@ -157,7 +157,7 @@ export function mkNestedTypes( register: Register, fhirSchema: RichFHIRSchema, logger?: CodegenLog, -): NestedType[] | undefined { +): NestedTypeSchema[] | undefined { if (!fhirSchema.elements) return undefined; const nested = collectNestedElements(fhirSchema, [], fhirSchema.elements).filter(([path, element]) => { @@ -172,7 +172,7 @@ export function mkNestedTypes( return true; }); - const nestedTypes = [] as NestedType[]; + const nestedTypes = [] as NestedTypeSchema[]; for (const [path, element] of nested) { const identifier = mkNestedIdentifier(register, fhirSchema, path); @@ -195,7 +195,7 @@ export function mkNestedTypes( const fields = transformNestedElements(register, fhirSchema, path, element.elements ?? {}, logger); - const nestedType: NestedType = { + const nestedType: NestedTypeSchema = { identifier, base, fields, @@ -208,7 +208,7 @@ export function mkNestedTypes( return nestedTypes.length === 0 ? undefined : nestedTypes; } -export function extractNestedDependencies(nestedTypes: NestedType[]): TypeIdentifier[] { +export function extractNestedDependencies(nestedTypes: NestedTypeSchema[]): TypeIdentifier[] { const deps: TypeIdentifier[] = []; for (const nested of nestedTypes) { diff --git a/src/typeschema/core/transformer.ts b/src/typeschema/core/transformer.ts index bf207969e..6f05fe6e1 100644 --- a/src/typeschema/core/transformer.ts +++ b/src/typeschema/core/transformer.ts @@ -15,13 +15,14 @@ import { type Field, type Identifier, isNestedIdentifier, + isPrimitiveIdentifier, isProfileIdentifier, - type NestedType, + isSpecializationIdentifier, + type NestedTypeSchema, type ProfileIdentifier, packageMetaToFhir, type RichFHIRSchema, type RichValueSet, - type SpecializationTypeSchema, type TypeIdentifier, type TypeSchema, type ValueSetTypeSchema, @@ -99,7 +100,7 @@ export async function transformValueSet( const collectRawDeps = ( base: TypeIdentifier | undefined, fields: Record | undefined, - nestedTypes: NestedType[] | undefined, + nestedTypes: NestedTypeSchema[] | undefined, ): TypeIdentifier[] => { const deps: TypeIdentifier[] = []; if (base) deps.push(base); @@ -112,7 +113,7 @@ export const extractDependencies = ( identifier: Identifier, base: TypeIdentifier | undefined, fields: Record | undefined, - nestedTypes: NestedType[] | undefined, + nestedTypes: NestedTypeSchema[] | undefined, ): Identifier[] | undefined => { const deps = collectRawDeps(base, fields, nestedTypes); @@ -129,7 +130,7 @@ export const extractProfileDependencies = ( identifier: ProfileIdentifier, base: TypeIdentifier | undefined, fields: Record | undefined, - nestedTypes: NestedType[] | undefined, + nestedTypes: NestedTypeSchema[] | undefined, ): TypeIdentifier[] | undefined => { const deps = collectRawDeps(base, fields, nestedTypes); const filtered = deps.filter((dep) => dep.url !== identifier.url); @@ -176,15 +177,29 @@ export function transformFhirSchema(register: Register, fhirSchema: RichFHIRSche } else { assert(!isNestedIdentifier(identifier), `Unexpected nested identifier for ${fhirSchema.url}`); const rawDeps = extractDependencies(identifier, base, fields, nested); - typeSchema = { - identifier, - base, - fields, - nested, - description: fhirSchema.description, - dependencies: rawDeps, - typeFamily: undefined, // NOTE: should be populateTypeFamily later. - } as TypeSchema; + if (isPrimitiveIdentifier(identifier)) { + assert(base, `Primitive type ${fhirSchema.url} must have a base type`); + typeSchema = { + identifier, + description: fhirSchema.description, + base, + dependencies: rawDeps, + }; + } else { + assert( + isSpecializationIdentifier(identifier), + `Unexpected identifier kind ${identifier.kind} for ${fhirSchema.url}`, + ); + typeSchema = { + identifier, + base, + fields, + nested, + description: fhirSchema.description, + dependencies: rawDeps, + typeFamily: undefined, // NOTE: should be populateTypeFamily later. + }; + } } const bindingSchemas = collectBindingSchemas(register, fhirSchema, logger); diff --git a/src/typeschema/ir/logic-promotion.ts b/src/typeschema/ir/logic-promotion.ts index f11f39419..033190bbf 100644 --- a/src/typeschema/ir/logic-promotion.ts +++ b/src/typeschema/ir/logic-promotion.ts @@ -8,7 +8,7 @@ import { isProfileTypeSchema, isSpecializationTypeSchema, isValueSetTypeSchema, - type NestedType, + type NestedTypeSchema, type PkgName, type TypeIdentifier, } from "@root/typeschema/types"; @@ -51,7 +51,7 @@ export const promoteLogical = (tsIndex: TypeSchemaIndex, promotes: LogicalPromot cloned.dependencies = cloned.dependencies?.map(replace); if (isSpecializationTypeSchema(cloned) || isProfileTypeSchema(cloned)) { cloned.fields = replaceInFields(cloned.fields); - cloned.nested = cloned.nested?.map((n: NestedType) => { + cloned.nested = cloned.nested?.map((n: NestedTypeSchema) => { return { ...n, base: replace(n.base), diff --git a/src/typeschema/ir/tree-shake.ts b/src/typeschema/ir/tree-shake.ts index 9133f5889..77669c9aa 100644 --- a/src/typeschema/ir/tree-shake.ts +++ b/src/typeschema/ir/tree-shake.ts @@ -192,12 +192,12 @@ export const treeShakeTypeSchema = (schema: TypeSchema, rule: TreeShakeRule, _lo if (rule.selectFields) { if (rule.ignoreFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule"); - mutableSelectFields(schema as SpecializationTypeSchema, rule.selectFields); + mutableSelectFields(schema, rule.selectFields); } if (rule.ignoreFields) { if (rule.selectFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule"); - mutableIgnoreFields(schema as SpecializationTypeSchema, rule.ignoreFields); + mutableIgnoreFields(schema, rule.ignoreFields); } if (isProfileTypeSchema(schema) && rule.ignoreExtensions) { @@ -220,7 +220,7 @@ export const treeShakeTypeSchema = (schema: TypeSchema, rule: TreeShakeRule, _lo } }); }; - collectUsedNestedTypes(schema as SpecializationTypeSchema); + collectUsedNestedTypes(schema); schema.nested = schema.nested.filter((n) => usedTypes.has(n.identifier.url)); } @@ -267,13 +267,8 @@ export const treeShake = (tsIndex: TypeSchemaIndex, treeShake: TreeShakeConf): T const id = JSON.stringify(depSchema.identifier); if (!acc[id]) newSchemas.push(depSchema); }); - if (schema.nested) { - for (const nest of schema.nested) { - if (isNestedIdentifier(nest.identifier)) continue; - const id = JSON.stringify(nest.identifier); - if (!acc[id]) newSchemas.push(nest as unknown as TypeSchema); - } - } + // NOTE: nested types' deps are already included in the parent's dependencies + // via extractNestedDependencies, so no need to collect them separately. } } return collectDeps(newSchemas, acc); diff --git a/src/typeschema/types.ts b/src/typeschema/types.ts index 15efb18d6..161540932 100644 --- a/src/typeschema/types.ts +++ b/src/typeschema/types.ts @@ -137,6 +137,12 @@ export const isProfileIdentifier = (id: TypeIdentifier | undefined): id is Profi return id?.kind === "profile"; }; +export const isSpecializationIdentifier = ( + id: TypeIdentifier | undefined, +): id is ResourceIdentifier | ComplexTypeIdentifier | LogicalIdentifier => { + return isResourceIdentifier(id) || isComplexTypeIdentifier(id) || isLogicalIdentifier(id); +}; + export const concatIdentifiers = ( ...sources: (T[] | undefined)[] ): T[] | undefined => { @@ -200,7 +206,7 @@ interface PrimitiveTypeSchema { dependencies?: TypeIdentifier[]; } -export interface NestedType { +export interface NestedTypeSchema { identifier: NestedIdentifier; base: TypeIdentifier; fields: Record; @@ -213,7 +219,7 @@ export interface ProfileTypeSchema { fields?: Record; extensions?: ProfileExtension[]; dependencies?: TypeIdentifier[]; - nested?: NestedType[]; + nested?: NestedTypeSchema[]; } export type DiscriminatorType = "value" | "exists" | "pattern" | "type" | "profile"; @@ -272,7 +278,7 @@ type SpecializationTypeSchemaBody = { base?: TypeIdentifier; description?: string; fields?: { [k: string]: Field }; - nested?: NestedType[]; + nested?: NestedTypeSchema[]; dependencies?: Identifier[]; /** Transitive children grouped by kind (e.g. Resource → { resources: [DomainResource, Patient, …] }) */ typeFamily?: TypeFamily; @@ -283,11 +289,13 @@ export type TypeFamily = { complexTypes?: ComplexTypeIdentifier[]; }; -export type ResourceTypeSchema = { identifier: ResourceIdentifier } & SpecializationTypeSchemaBody; -export type ComplexTypeTypeSchema = { identifier: ComplexTypeIdentifier } & SpecializationTypeSchemaBody; -export type LogicalTypeSchema = { identifier: LogicalIdentifier } & SpecializationTypeSchemaBody; +export type SpecializationTypeSchema = { + identifier: ResourceIdentifier | ComplexTypeIdentifier | LogicalIdentifier; +} & SpecializationTypeSchemaBody; -export type SpecializationTypeSchema = ResourceTypeSchema | ComplexTypeTypeSchema | LogicalTypeSchema; +export type ResourceTypeSchema = SpecializationTypeSchema & { identifier: ResourceIdentifier }; +export type ComplexTypeTypeSchema = SpecializationTypeSchema & { identifier: ComplexTypeIdentifier }; +export type LogicalTypeSchema = SpecializationTypeSchema & { identifier: LogicalIdentifier }; export interface RegularField { type: TypeIdentifier; diff --git a/src/typeschema/utils.ts b/src/typeschema/utils.ts index 595346a42..651462683 100644 --- a/src/typeschema/utils.ts +++ b/src/typeschema/utils.ts @@ -20,7 +20,7 @@ import { isResourceTypeSchema, isSpecializationTypeSchema, type LogicalTypeSchema, - type NestedType, + type NestedTypeSchema, type PkgName, type ProfileExtension, type ProfileTypeSchema, @@ -193,7 +193,7 @@ export const mkTypeSchemaIndex = ( }, ): TypeSchemaIndex => { const index: Record> = {}; - const nestedIndex: Record> = {}; + const nestedIndex: Record> = {}; const append = (schema: TypeSchema) => { const url = schema.identifier.url; const pkg = schema.identifier.package; diff --git a/test/api/write-generator/__snapshots__/introspection.test.ts.snap b/test/api/write-generator/__snapshots__/introspection.test.ts.snap index 396621700..b9ffd2009 100644 --- a/test/api/write-generator/__snapshots__/introspection.test.ts.snap +++ b/test/api/write-generator/__snapshots__/introspection.test.ts.snap @@ -1715,24 +1715,24 @@ exports[`IntrospectionWriter - TypeSchema output Check all introspection data in {"identifier":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"DomainResource","url":"http://hl7.org/fhir/StructureDefinition/DomainResource"},"base":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"fields":{"text":{"type":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Narrative","url":"http://hl7.org/fhir/StructureDefinition/Narrative"},"required":false,"excluded":false,"array":false},"contained":{"type":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"required":false,"excluded":false,"array":true}},"description":"A resource that includes narrative, extensions, and contained resources.","dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Narrative","url":"http://hl7.org/fhir/StructureDefinition/Narrative"},{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"}],"typeFamily":{"resources":[{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"OperationOutcome","url":"http://hl7.org/fhir/StructureDefinition/OperationOutcome"}]}} {"identifier":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"fields":{},"description":"Base StructureDefinition for BackboneElement Type: Base definition for all elements that are defined inside a resource - but not those in a data type.","dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"}],"typeFamily":{"complexTypes":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Dosage","url":"http://hl7.org/fhir/StructureDefinition/Dosage"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"ElementDefinition","url":"http://hl7.org/fhir/StructureDefinition/ElementDefinition"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"MarketingStatus","url":"http://hl7.org/fhir/StructureDefinition/MarketingStatus"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Population","url":"http://hl7.org/fhir/StructureDefinition/Population"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"ProdCharacteristic","url":"http://hl7.org/fhir/StructureDefinition/ProdCharacteristic"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"ProductShelfLife","url":"http://hl7.org/fhir/StructureDefinition/ProductShelfLife"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"SubstanceAmount","url":"http://hl7.org/fhir/StructureDefinition/SubstanceAmount"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Timing","url":"http://hl7.org/fhir/StructureDefinition/Timing"}]}} {"identifier":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"fields":{"id":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false}},"description":"Base StructureDefinition for Element Type: Base definition for all elements in a resource.","dependencies":[{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"}],"typeFamily":{"complexTypes":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"BackboneElement","url":"http://hl7.org/fhir/StructureDefinition/BackboneElement"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"CodeableConcept","url":"http://hl7.org/fhir/StructureDefinition/CodeableConcept"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Narrative","url":"http://hl7.org/fhir/StructureDefinition/Narrative"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Coding","url":"http://hl7.org/fhir/StructureDefinition/Coding"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Meta","url":"http://hl7.org/fhir/StructureDefinition/Meta"}]}} -{"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},"base":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"description":"Base StructureDefinition for code type: A string which has at least one character and no leading or trailing whitespace and where there is no whitespace other than single spaces in the contents","dependencies":[{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"}]} +{"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},"description":"Base StructureDefinition for code type: A string which has at least one character and no leading or trailing whitespace and where there is no whitespace other than single spaces in the contents","base":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"dependencies":[{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"}]} {"identifier":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"CodeableConcept","url":"http://hl7.org/fhir/StructureDefinition/CodeableConcept"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"fields":{"coding":{"type":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Coding","url":"http://hl7.org/fhir/StructureDefinition/Coding"},"required":false,"excluded":false,"array":true},"text":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false}},"description":"Base StructureDefinition for CodeableConcept Type: A concept that may be defined by a formal reference to a terminology or ontology or may be provided by text.","dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Coding","url":"http://hl7.org/fhir/StructureDefinition/Coding"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"}]} -{"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"description":"Base StructureDefinition for string Type: A sequence of Unicode characters","dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"}]} +{"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"description":"Base StructureDefinition for string Type: A sequence of Unicode characters","base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"}]} {"identifier":{"kind":"binding","package":"shared","version":"1.0.0","name":"IssueDetails","url":"urn:fhir:binding:IssueDetails"},"valueset":{"kind":"value-set","package":"hl7.fhir.r4.core","version":"4.0.1","name":"OperationOutcome","url":"http://hl7.org/fhir/ValueSet/operation-outcome"},"strength":"example","dependencies":[{"kind":"value-set","package":"hl7.fhir.r4.core","version":"4.0.1","name":"OperationOutcome","url":"http://hl7.org/fhir/ValueSet/operation-outcome"}]} {"identifier":{"kind":"binding","package":"shared","version":"1.0.0","name":"IssueSeverity","url":"urn:fhir:binding:IssueSeverity"},"valueset":{"kind":"value-set","package":"hl7.fhir.r4.core","version":"4.0.1","name":"IssueSeverity","url":"http://hl7.org/fhir/ValueSet/issue-severity"},"strength":"required","enum":{"isOpen":false,"values":["fatal","error","warning","information"]},"dependencies":[{"kind":"value-set","package":"hl7.fhir.r4.core","version":"4.0.1","name":"IssueSeverity","url":"http://hl7.org/fhir/ValueSet/issue-severity"}]} {"identifier":{"kind":"binding","package":"shared","version":"1.0.0","name":"IssueType","url":"urn:fhir:binding:IssueType"},"valueset":{"kind":"value-set","package":"hl7.fhir.r4.core","version":"4.0.1","name":"IssueType","url":"http://hl7.org/fhir/ValueSet/issue-type"},"strength":"required","enum":{"isOpen":false,"values":["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","incomplete","throttled","informational"]},"dependencies":[{"kind":"value-set","package":"hl7.fhir.r4.core","version":"4.0.1","name":"IssueType","url":"http://hl7.org/fhir/ValueSet/issue-type"}]} {"identifier":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Narrative","url":"http://hl7.org/fhir/StructureDefinition/Narrative"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"fields":{"status":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},"required":true,"excluded":false,"array":false,"binding":{"kind":"binding","package":"shared","version":"1.0.0","name":"NarrativeStatus","url":"urn:fhir:binding:NarrativeStatus"},"enum":{"isOpen":false,"values":["generated","extensions","additional","empty"]}},"div":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"xhtml","url":"http://hl7.org/fhir/StructureDefinition/xhtml"},"required":true,"excluded":false,"array":false}},"description":"Base StructureDefinition for Narrative Type: A human-readable summary of the resource conveying the essential clinical and business information for the resource.","dependencies":[{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"xhtml","url":"http://hl7.org/fhir/StructureDefinition/xhtml"},{"kind":"binding","package":"shared","version":"1.0.0","name":"NarrativeStatus","url":"urn:fhir:binding:NarrativeStatus"}]} {"identifier":{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Resource","url":"http://hl7.org/fhir/StructureDefinition/Resource"},"fields":{"id":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false},"meta":{"type":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Meta","url":"http://hl7.org/fhir/StructureDefinition/Meta"},"required":false,"excluded":false,"array":false},"implicitRules":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},"required":false,"excluded":false,"array":false},"language":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},"required":false,"excluded":false,"array":false,"binding":{"kind":"binding","package":"shared","version":"1.0.0","name":"Language","url":"urn:fhir:binding:Language"},"enum":{"isOpen":true,"values":["ar","bn","cs","da","de","de-AT","de-CH","de-DE","el","en","en-AU","en-CA","en-GB","en-IN","en-NZ","en-SG","en-US","es","es-AR","es-ES","es-UY","fi","fr","fr-BE","fr-CH","fr-FR","fy","fy-NL","hi","hr","it","it-CH","it-IT","ja","ko","nl","nl-BE","nl-NL","no","no-NO","pa","pl","pt","pt-BR","ru","ru-RU","sr","sr-RS","sv","sv-SE","te","zh","zh-CN","zh-HK","zh-SG","zh-TW"]}}},"description":"This is the base resource type for everything.","dependencies":[{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Meta","url":"http://hl7.org/fhir/StructureDefinition/Meta"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},{"kind":"binding","package":"shared","version":"1.0.0","name":"Language","url":"urn:fhir:binding:Language"}],"typeFamily":{"resources":[{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"DomainResource","url":"http://hl7.org/fhir/StructureDefinition/DomainResource"},{"kind":"resource","package":"hl7.fhir.r4.core","version":"4.0.1","name":"OperationOutcome","url":"http://hl7.org/fhir/StructureDefinition/OperationOutcome"}]}} {"identifier":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Coding","url":"http://hl7.org/fhir/StructureDefinition/Coding"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"fields":{"system":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},"required":false,"excluded":false,"array":false},"version":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false},"code":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},"required":false,"excluded":false,"array":false},"display":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"required":false,"excluded":false,"array":false},"userSelected":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"boolean","url":"http://hl7.org/fhir/StructureDefinition/boolean"},"required":false,"excluded":false,"array":false}},"description":"Base StructureDefinition for Coding Type: A reference to a code defined by a terminology system.","dependencies":[{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"boolean","url":"http://hl7.org/fhir/StructureDefinition/boolean"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"code","url":"http://hl7.org/fhir/StructureDefinition/code"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"}]} -{"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"xhtml","url":"http://hl7.org/fhir/StructureDefinition/xhtml"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"description":"Base StructureDefinition for xhtml Type","dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"}]} +{"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"xhtml","url":"http://hl7.org/fhir/StructureDefinition/xhtml"},"description":"Base StructureDefinition for xhtml Type","base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"}]} {"identifier":{"kind":"binding","package":"shared","version":"1.0.0","name":"NarrativeStatus","url":"urn:fhir:binding:NarrativeStatus"},"valueset":{"kind":"value-set","package":"hl7.fhir.r4.core","version":"4.0.1","name":"NarrativeStatus","url":"http://hl7.org/fhir/ValueSet/narrative-status"},"strength":"required","enum":{"isOpen":false,"values":["generated","extensions","additional","empty"]},"dependencies":[{"kind":"value-set","package":"hl7.fhir.r4.core","version":"4.0.1","name":"NarrativeStatus","url":"http://hl7.org/fhir/ValueSet/narrative-status"}]} {"identifier":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Meta","url":"http://hl7.org/fhir/StructureDefinition/Meta"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"fields":{"versionId":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"id","url":"http://hl7.org/fhir/StructureDefinition/id"},"required":false,"excluded":false,"array":false},"lastUpdated":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"instant","url":"http://hl7.org/fhir/StructureDefinition/instant"},"required":false,"excluded":false,"array":false},"source":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},"required":false,"excluded":false,"array":false},"profile":{"type":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"canonical","url":"http://hl7.org/fhir/StructureDefinition/canonical"},"required":false,"excluded":false,"array":true},"security":{"type":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Coding","url":"http://hl7.org/fhir/StructureDefinition/Coding"},"required":false,"excluded":false,"array":true,"binding":{"kind":"binding","package":"shared","version":"1.0.0","name":"SecurityLabels","url":"urn:fhir:binding:SecurityLabels"}},"tag":{"type":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Coding","url":"http://hl7.org/fhir/StructureDefinition/Coding"},"required":false,"excluded":false,"array":true,"binding":{"kind":"binding","package":"shared","version":"1.0.0","name":"Tags","url":"urn:fhir:binding:Tags"}}},"description":"Base StructureDefinition for Meta Type: The metadata about a resource. This is content in the resource that is maintained by the infrastructure. Changes to the content might not always be associated with version changes to the resource.","dependencies":[{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"canonical","url":"http://hl7.org/fhir/StructureDefinition/canonical"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Coding","url":"http://hl7.org/fhir/StructureDefinition/Coding"},{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"id","url":"http://hl7.org/fhir/StructureDefinition/id"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"instant","url":"http://hl7.org/fhir/StructureDefinition/instant"},{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},{"kind":"binding","package":"shared","version":"1.0.0","name":"SecurityLabels","url":"urn:fhir:binding:SecurityLabels"},{"kind":"binding","package":"shared","version":"1.0.0","name":"Tags","url":"urn:fhir:binding:Tags"}]} -{"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"description":"Base StructureDefinition for uri Type: String of characters used to identify a name or a resource","dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"}]} +{"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},"description":"Base StructureDefinition for uri Type: String of characters used to identify a name or a resource","base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"}]} {"identifier":{"kind":"binding","package":"shared","version":"1.0.0","name":"Language","url":"urn:fhir:binding:Language"},"valueset":{"kind":"value-set","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Languages","url":"http://hl7.org/fhir/ValueSet/languages"},"strength":"preferred","enum":{"isOpen":true,"values":["ar","bn","cs","da","de","de-AT","de-CH","de-DE","el","en","en-AU","en-CA","en-GB","en-IN","en-NZ","en-SG","en-US","es","es-AR","es-ES","es-UY","fi","fr","fr-BE","fr-CH","fr-FR","fy","fy-NL","hi","hr","it","it-CH","it-IT","ja","ko","nl","nl-BE","nl-NL","no","no-NO","pa","pl","pt","pt-BR","ru","ru-RU","sr","sr-RS","sv","sv-SE","te","zh","zh-CN","zh-HK","zh-SG","zh-TW"]},"dependencies":[{"kind":"value-set","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Languages","url":"http://hl7.org/fhir/ValueSet/languages"}]} -{"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"boolean","url":"http://hl7.org/fhir/StructureDefinition/boolean"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"description":"Base StructureDefinition for boolean Type: Value of \\"true\\" or \\"false\\"","dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"}]} -{"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"canonical","url":"http://hl7.org/fhir/StructureDefinition/canonical"},"base":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},"description":"Base StructureDefinition for canonical type: A URI that is a reference to a canonical URL on a FHIR resource","dependencies":[{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"}]} -{"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"id","url":"http://hl7.org/fhir/StructureDefinition/id"},"base":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"description":"Base StructureDefinition for id type: Any combination of letters, numerals, \\"-\\" and \\".\\", with a length limit of 64 characters. (This might be an integer, an unprefixed OID, UUID or any other identifier pattern that meets these constraints.) Ids are case-insensitive.","dependencies":[{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"}]} -{"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"instant","url":"http://hl7.org/fhir/StructureDefinition/instant"},"base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"description":"Base StructureDefinition for instant Type: An instant in time - known at least to the second","dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"}]} +{"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"boolean","url":"http://hl7.org/fhir/StructureDefinition/boolean"},"description":"Base StructureDefinition for boolean Type: Value of \\"true\\" or \\"false\\"","base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"}]} +{"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"canonical","url":"http://hl7.org/fhir/StructureDefinition/canonical"},"description":"Base StructureDefinition for canonical type: A URI that is a reference to a canonical URL on a FHIR resource","base":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"},"dependencies":[{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"uri","url":"http://hl7.org/fhir/StructureDefinition/uri"}]} +{"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"id","url":"http://hl7.org/fhir/StructureDefinition/id"},"description":"Base StructureDefinition for id type: Any combination of letters, numerals, \\"-\\" and \\".\\", with a length limit of 64 characters. (This might be an integer, an unprefixed OID, UUID or any other identifier pattern that meets these constraints.) Ids are case-insensitive.","base":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"},"dependencies":[{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"string","url":"http://hl7.org/fhir/StructureDefinition/string"}]} +{"identifier":{"kind":"primitive-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"instant","url":"http://hl7.org/fhir/StructureDefinition/instant"},"description":"Base StructureDefinition for instant Type: An instant in time - known at least to the second","base":{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"},"dependencies":[{"kind":"complex-type","package":"hl7.fhir.r4.core","version":"4.0.1","name":"Element","url":"http://hl7.org/fhir/StructureDefinition/Element"}]} {"identifier":{"kind":"binding","package":"shared","version":"1.0.0","name":"SecurityLabels","url":"urn:fhir:binding:SecurityLabels"},"valueset":{"kind":"value-set","package":"hl7.fhir.r4.core","version":"4.0.1","name":"SecurityLabels","url":"http://hl7.org/fhir/ValueSet/security-labels"},"strength":"extensible","dependencies":[{"kind":"value-set","package":"hl7.fhir.r4.core","version":"4.0.1","name":"SecurityLabels","url":"http://hl7.org/fhir/ValueSet/security-labels"}]} {"identifier":{"kind":"binding","package":"shared","version":"1.0.0","name":"Tags","url":"urn:fhir:binding:Tags"},"valueset":{"kind":"value-set","package":"hl7.fhir.r4.core","version":"4.0.1","name":"CommonTags","url":"http://hl7.org/fhir/ValueSet/common-tags"},"strength":"example","dependencies":[{"kind":"value-set","package":"hl7.fhir.r4.core","version":"4.0.1","name":"CommonTags","url":"http://hl7.org/fhir/ValueSet/common-tags"}]} " diff --git a/test/unit/typeschema/__snapshots__/snapshot.test.ts.snap b/test/unit/typeschema/__snapshots__/snapshot.test.ts.snap index 89eff6294..bf7645ce9 100644 --- a/test/unit/typeschema/__snapshots__/snapshot.test.ts.snap +++ b/test/unit/typeschema/__snapshots__/snapshot.test.ts.snap @@ -942,6 +942,7 @@ exports[`FHIR Schema to Type Schema (snapshot) Real world examples string primit "name": "string", "url": "http://hl7.org/fhir/StructureDefinition/string" }, + "description": "Base StructureDefinition for string Type: A sequence of Unicode characters", "base": { "kind": "complex-type", "package": "hl7.fhir.r4.core", @@ -949,7 +950,6 @@ exports[`FHIR Schema to Type Schema (snapshot) Real world examples string primit "name": "Element", "url": "http://hl7.org/fhir/StructureDefinition/Element" }, - "description": "Base StructureDefinition for string Type: A sequence of Unicode characters", "dependencies": [ { "kind": "complex-type", diff --git a/test/unit/typeschema/r4.test.ts b/test/unit/typeschema/r4.test.ts index 7ecfafaa9..b1180ab0d 100644 --- a/test/unit/typeschema/r4.test.ts +++ b/test/unit/typeschema/r4.test.ts @@ -55,7 +55,7 @@ describe("TypeSchema R4 generation", async () => { if (!md) { throw new Error("markdown type not found"); } - const ts = (await registerFsAndMkTs(r4, md, logger))[0] as SpecializationTypeSchema; + const ts = (await registerFsAndMkTs(r4, md, logger))[0]; expect(ts).toMatchObject({ identifier: { kind: "primitive-type", @@ -63,8 +63,6 @@ describe("TypeSchema R4 generation", async () => { url: "http://hl7.org/fhir/StructureDefinition/markdown", }, base: { url: "http://hl7.org/fhir/StructureDefinition/string" }, - fields: undefined, - nested: undefined, dependencies: [{ url: "http://hl7.org/fhir/StructureDefinition/string" }], }); }); diff --git a/test/unit/typeschema/transformer.test.ts b/test/unit/typeschema/transformer.test.ts index 833e7599f..810947456 100644 --- a/test/unit/typeschema/transformer.test.ts +++ b/test/unit/typeschema/transformer.test.ts @@ -58,6 +58,7 @@ describe("TypeSchema Transformer Core Logic", async () => { name: "string", type: "string", kind: "primitive-type", + base: "http://hl7.org/fhir/StructureDefinition/Element", url: "http://hl7.org/fhir/StructureDefinition/string", class: "", }; diff --git a/test/unit/typeschema/transformer/r4.test.ts b/test/unit/typeschema/transformer/r4.test.ts index 3b914632a..a8b6b51e1 100644 --- a/test/unit/typeschema/transformer/r4.test.ts +++ b/test/unit/typeschema/transformer/r4.test.ts @@ -55,7 +55,7 @@ describe("TypeSchema R4 generation", async () => { if (!md) { throw new Error("markdown type not found"); } - const ts = (await registerFsAndMkTs(r4, md, logger))[0] as SpecializationTypeSchema; + const ts = (await registerFsAndMkTs(r4, md, logger))[0]; expect(ts).toMatchObject({ identifier: { kind: "primitive-type", @@ -63,8 +63,6 @@ describe("TypeSchema R4 generation", async () => { url: "http://hl7.org/fhir/StructureDefinition/markdown", }, base: { url: "http://hl7.org/fhir/StructureDefinition/string" }, - fields: undefined, - nested: undefined, dependencies: [{ url: "http://hl7.org/fhir/StructureDefinition/string" }], }); }); diff --git a/test/unit/typeschema/utils.test.ts b/test/unit/typeschema/utils.test.ts index 9c53cff53..8d44ff1a0 100644 --- a/test/unit/typeschema/utils.test.ts +++ b/test/unit/typeschema/utils.test.ts @@ -6,7 +6,6 @@ import type { RegularField, SpecializationTypeSchema, TypeIdentifier, - TypeSchema, } from "@typeschema/types"; import { mkTypeSchemaIndex } from "@typeschema/utils"; @@ -81,16 +80,15 @@ describe("TypeSchema Index", () => { }); it("should handle a schema without a base reference", () => { - const bSchema: TypeSchema = { + const bSchema: SpecializationTypeSchema = { identifier: { name: "B" as Name, package: "test", - kind: "profile", + kind: "resource", version: "1.0.0", url: "http://example.org/StructureDefinition/B" as CanonicalUrl, }, - // No base provided - } as TypeSchema; + }; const index = mkTypeSchemaIndex([bSchema], {}); const result = index.hierarchy(bSchema); From abf41a16e3cebe8d15940a6cc5a3e4e836853b16 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Tue, 24 Mar 2026 15:07:12 +0100 Subject: [PATCH 3/7] ref: Make RichFHIRSchema a discriminated union on derivation and kind - Split RichFHIRSchema into RichProfileFHIRSchema (derivation: "constraint") and RichSpecializationFHIRSchema (derivation: "specialization") - Split RichSpecializationFHIRSchema into per-kind members: RichPrimitiveFHIRSchema, RichComplexTypeFHIRSchema, RichResourceFHIRSchema, RichLogicalFHIRSchema - enrichFHIRSchema normalizes derivation and kind at the boundary - mkIdentifier overloads now use named union members, enabling control-flow narrowing to resolve the correct return type - transformer branches on derivation/kind directly, removing runtime asserts that the type system now enforces --- src/typeschema/core/identifier.ts | 51 ++++++++++++------- src/typeschema/core/transformer.ts | 51 ++++++++----------- src/typeschema/types.ts | 24 ++++++++- .../__snapshots__/introspection.test.ts.snap | 12 ++--- 4 files changed, 83 insertions(+), 55 deletions(-) diff --git a/src/typeschema/core/identifier.ts b/src/typeschema/core/identifier.ts index 615c5b4aa..30f5164c5 100644 --- a/src/typeschema/core/identifier.ts +++ b/src/typeschema/core/identifier.ts @@ -8,11 +8,21 @@ import type { FHIRSchemaElement } from "@atomic-ehr/fhirschema"; import type { BindingIdentifier, CanonicalUrl, + ComplexTypeIdentifier, + Identifier, + LogicalIdentifier, Name, PackageMeta, + PrimitiveIdentifier, + ProfileIdentifier, + ResourceIdentifier, + RichComplexTypeFHIRSchema, RichFHIRSchema, + RichLogicalFHIRSchema, + RichPrimitiveFHIRSchema, + RichProfileFHIRSchema, + RichResourceFHIRSchema, RichValueSet, - TypeIdentifier, ValueSetIdentifier, } from "@typeschema/types"; import type { Register } from "../register"; @@ -27,23 +37,30 @@ function getVersionFromUrl(url: CanonicalUrl): string | undefined { return version; } -function determineKind(fhirSchema: RichFHIRSchema): TypeIdentifier["kind"] { - if (fhirSchema.derivation === "constraint") return "profile"; - if (fhirSchema.kind === "primitive-type") return "primitive-type"; - if (fhirSchema.kind === "complex-type") return "complex-type"; - if (fhirSchema.kind === "resource") return "resource"; - if (fhirSchema.kind === "logical") return "logical"; - return "resource"; -} +const identifierBase = (fhirSchema: RichFHIRSchema) => ({ + package: fhirSchema.package_meta.name, + version: fhirSchema.package_meta.version, + name: fhirSchema.name, + url: fhirSchema.url, +}); -export function mkIdentifier(fhirSchema: RichFHIRSchema): TypeIdentifier { - return { - kind: determineKind(fhirSchema), - package: fhirSchema.package_meta.name, - version: fhirSchema.package_meta.version, - name: fhirSchema.name, - url: fhirSchema.url, - }; +export function mkIdentifier(fhirSchema: RichProfileFHIRSchema): ProfileIdentifier; +export function mkIdentifier(fhirSchema: RichPrimitiveFHIRSchema): PrimitiveIdentifier; +export function mkIdentifier(fhirSchema: RichComplexTypeFHIRSchema): ComplexTypeIdentifier; +export function mkIdentifier(fhirSchema: RichResourceFHIRSchema): ResourceIdentifier; +export function mkIdentifier(fhirSchema: RichLogicalFHIRSchema): LogicalIdentifier; +export function mkIdentifier( + fhirSchema: RichComplexTypeFHIRSchema | RichResourceFHIRSchema | RichLogicalFHIRSchema, +): ComplexTypeIdentifier | ResourceIdentifier | LogicalIdentifier; +export function mkIdentifier(fhirSchema: RichFHIRSchema): Identifier; +export function mkIdentifier(fhirSchema: RichFHIRSchema): Identifier { + const fields = identifierBase(fhirSchema); + if (fhirSchema.derivation === "constraint") return { kind: "profile", ...fields }; + if (fhirSchema.kind === "primitive-type") return { kind: "primitive-type", ...fields }; + if (fhirSchema.kind === "complex-type") return { kind: "complex-type", ...fields }; + if (fhirSchema.kind === "resource") return { kind: "resource", ...fields }; + if (fhirSchema.kind === "logical") return { kind: "logical", ...fields }; + return { kind: "resource", ...fields }; } const getValueSetName = (url: CanonicalUrl): Name => { diff --git a/src/typeschema/core/transformer.ts b/src/typeschema/core/transformer.ts index 6f05fe6e1..3b96786ec 100644 --- a/src/typeschema/core/transformer.ts +++ b/src/typeschema/core/transformer.ts @@ -15,9 +15,6 @@ import { type Field, type Identifier, isNestedIdentifier, - isPrimitiveIdentifier, - isProfileIdentifier, - isSpecializationIdentifier, type NestedTypeSchema, type ProfileIdentifier, packageMetaToFhir, @@ -138,8 +135,6 @@ export const extractProfileDependencies = ( }; export function transformFhirSchema(register: Register, fhirSchema: RichFHIRSchema, logger?: CodegenLog): TypeSchema[] { - const identifier = mkIdentifier(fhirSchema); - let base: Identifier | undefined; if (fhirSchema.base) { const baseFs = register.resolveFs( @@ -160,8 +155,8 @@ export function transformFhirSchema(register: Register, fhirSchema: RichFHIRSche let typeSchema: TypeSchema; if (fhirSchema.derivation === "constraint") { + const identifier = mkIdentifier(fhirSchema); if (!base) throw new Error(`Profile ${fhirSchema.url} must have a base type`); - assert(isProfileIdentifier(identifier)); const extensions = extractProfileExtensions(register, fhirSchema, logger); const extensionDeps = extensions?.flatMap(extractExtensionDeps); const rawDeps = extractProfileDependencies(identifier, base, fields, nested); @@ -174,32 +169,28 @@ export function transformFhirSchema(register: Register, fhirSchema: RichFHIRSche dependencies: concatIdentifiers(rawDeps, extensionDeps), extensions, }; + } else if (fhirSchema.kind === "primitive-type") { + const identifier = mkIdentifier(fhirSchema); + const rawDeps = extractDependencies(identifier, base, fields, nested); + assert(base, `Primitive type ${fhirSchema.url} must have a base type`); + typeSchema = { + identifier, + description: fhirSchema.description, + base, + dependencies: rawDeps, + }; } else { - assert(!isNestedIdentifier(identifier), `Unexpected nested identifier for ${fhirSchema.url}`); + const identifier = mkIdentifier(fhirSchema); const rawDeps = extractDependencies(identifier, base, fields, nested); - if (isPrimitiveIdentifier(identifier)) { - assert(base, `Primitive type ${fhirSchema.url} must have a base type`); - typeSchema = { - identifier, - description: fhirSchema.description, - base, - dependencies: rawDeps, - }; - } else { - assert( - isSpecializationIdentifier(identifier), - `Unexpected identifier kind ${identifier.kind} for ${fhirSchema.url}`, - ); - typeSchema = { - identifier, - base, - fields, - nested, - description: fhirSchema.description, - dependencies: rawDeps, - typeFamily: undefined, // NOTE: should be populateTypeFamily later. - }; - } + typeSchema = { + identifier, + base, + fields, + nested, + description: fhirSchema.description, + dependencies: rawDeps, + typeFamily: undefined, // NOTE: should be populateTypeFamily later. + }; } const bindingSchemas = collectBindingSchemas(register, fhirSchema, logger); diff --git a/src/typeschema/types.ts b/src/typeschema/types.ts index 161540932..c042408c2 100644 --- a/src/typeschema/types.ts +++ b/src/typeschema/types.ts @@ -69,16 +69,36 @@ export type RichStructureDefinition = Omit & { url: CanonicalUrl; }; -export type RichFHIRSchema = Omit & { +export type FHIRSchemaKind = "primitive-type" | "complex-type" | "resource" | "logical"; + +type RichFHIRSchemaBase = Omit & { package_meta: PackageMeta; name: Name; url: CanonicalUrl; base: CanonicalUrl; + kind: FHIRSchemaKind; }; +export type RichProfileFHIRSchema = RichFHIRSchemaBase & { derivation: "constraint" }; + +export type RichPrimitiveFHIRSchema = RichFHIRSchemaBase & { derivation: "specialization"; kind: "primitive-type" }; +export type RichComplexTypeFHIRSchema = RichFHIRSchemaBase & { derivation: "specialization"; kind: "complex-type" }; +export type RichResourceFHIRSchema = RichFHIRSchemaBase & { derivation: "specialization"; kind: "resource" }; +export type RichLogicalFHIRSchema = RichFHIRSchemaBase & { derivation: "specialization"; kind: "logical" }; +export type RichSpecializationFHIRSchema = + | RichPrimitiveFHIRSchema + | RichComplexTypeFHIRSchema + | RichResourceFHIRSchema + | RichLogicalFHIRSchema; + +export type RichFHIRSchema = RichProfileFHIRSchema | RichSpecializationFHIRSchema; + export const enrichFHIRSchema = (schema: FS.FHIRSchema, packageMeta: PackageMeta): RichFHIRSchema => { + const derivation = schema.derivation === "constraint" ? ("constraint" as const) : ("specialization" as const); return { ...schema, + derivation, + kind: schema.kind as FHIRSchemaKind, package_meta: schema.package_meta || packageMeta, name: schema.name as Name, url: schema.url as CanonicalUrl, @@ -93,7 +113,7 @@ type IdentifierBase = { version: PkgVersion; }; -type PrimitiveIdentifier = { kind: "primitive-type" } & IdentifierBase; +export type PrimitiveIdentifier = { kind: "primitive-type" } & IdentifierBase; export type ComplexTypeIdentifier = { kind: "complex-type" } & IdentifierBase; export type ResourceIdentifier = { kind: "resource" } & IdentifierBase; export type ValueSetIdentifier = { kind: "value-set" } & IdentifierBase; diff --git a/test/api/write-generator/__snapshots__/introspection.test.ts.snap b/test/api/write-generator/__snapshots__/introspection.test.ts.snap index b9ffd2009..9d1d42651 100644 --- a/test/api/write-generator/__snapshots__/introspection.test.ts.snap +++ b/test/api/write-generator/__snapshots__/introspection.test.ts.snap @@ -906,7 +906,7 @@ exports[`IntrospectionWriter - Fhir Schema Output Check all introspection data i {"name":"date","type":"date","kind":"primitive-type","class":"primitive-type","url":"http://hl7.org/fhir/StructureDefinition/date","version":"4.0.1","description":"Base StructureDefinition for date Type: A date or partial date (e.g. just year or year + month). There is no time zone. The format is a union of the schema types gYear, gYearMonth and date. Dates SHALL be valid dates.","derivation":"specialization","base":"http://hl7.org/fhir/StructureDefinition/Element","package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"dateTime","type":"dateTime","kind":"primitive-type","class":"primitive-type","url":"http://hl7.org/fhir/StructureDefinition/dateTime","version":"4.0.1","description":"Base StructureDefinition for dateTime Type: A date, date-time or partial date (e.g. just year or year + month). If hours and minutes are specified, a time zone SHALL be populated. The format is a union of the schema types gYear, gYearMonth, date and dateTime. Seconds must be provided due to schema type constraints but may be zero-filled and may be ignored. Dates SHALL be valid dates.","derivation":"specialization","base":"http://hl7.org/fhir/StructureDefinition/Element","package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"decimal","type":"decimal","kind":"primitive-type","class":"primitive-type","url":"http://hl7.org/fhir/StructureDefinition/decimal","version":"4.0.1","description":"Base StructureDefinition for decimal Type: A rational number with implicit precision","derivation":"specialization","base":"http://hl7.org/fhir/StructureDefinition/Element","package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} -{"name":"Definition","type":"Definition","kind":"logical","class":"logical","url":"http://hl7.org/fhir/StructureDefinition/Definition","version":"4.0.1","description":"Logical Model: A pattern to be followed by resources that represent a specific proposal, plan and/or order for some sort of action or service.","package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} +{"name":"Definition","type":"Definition","kind":"logical","class":"logical","url":"http://hl7.org/fhir/StructureDefinition/Definition","version":"4.0.1","description":"Logical Model: A pattern to be followed by resources that represent a specific proposal, plan and/or order for some sort of action or service.","derivation":"specialization","package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"Design Note","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/designNote","version":"4.0.1","description":"Information captured by the author/maintainer of the questionnaire for development purposes, not intended to be seen by users.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/designNote"},"index":1},"value":{"choices":["valueMarkdown"],"index":3},"valueMarkdown":{"type":"markdown","choiceOf":"value","index":4}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"DetectedIssue","type":"DetectedIssue","kind":"resource","class":"resource","url":"http://hl7.org/fhir/StructureDefinition/DetectedIssue","version":"4.0.1","description":"Indicates an actual or potential clinical issue with or between one or more active or proposed clinical actions for a patient; e.g. Drug-drug interaction, Ineffective treatment frequency, Procedure-condition conflict, etc.","derivation":"specialization","base":"http://hl7.org/fhir/StructureDefinition/DomainResource","elements":{"identifier":{"short":"Unique id for the detected issue","type":"Identifier","isSummary":true,"array":true,"index":0},"status":{"short":"registered | preliminary | final | amended +","type":"code","isModifier":true,"isModifierReason":"This element is labeled as a modifier because it is a status element that contains status entered-in-error which means that the resource should not be treated as valid","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/observation-status|4.0.1","bindingName":"DetectedIssueStatus"},"index":1},"code":{"short":"Issue Category, e.g. drug-drug, duplicate therapy, etc.","type":"CodeableConcept","isSummary":true,"binding":{"strength":"preferred","valueSet":"http://hl7.org/fhir/ValueSet/detectedissue-category","bindingName":"DetectedIssueCategory"},"index":2},"severity":{"short":"high | moderate | low","type":"code","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/detectedissue-severity|4.0.1","bindingName":"DetectedIssueSeverity"},"index":3},"patient":{"short":"Associated patient","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/Patient"],"index":4},"identified":{"short":"When identified","isSummary":true,"choices":["identifiedDateTime","identifiedPeriod"],"index":6},"identifiedDateTime":{"short":"When identified","type":"dateTime","isSummary":true,"choiceOf":"identified","index":7},"identifiedPeriod":{"short":"When identified","type":"Period","isSummary":true,"choiceOf":"identified","index":8},"author":{"short":"The provider or device that identified the issue","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/Device","http://hl7.org/fhir/StructureDefinition/Practitioner","http://hl7.org/fhir/StructureDefinition/PractitionerRole"],"index":9},"implicated":{"short":"Problem resource","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/Resource"],"array":true,"index":10},"evidence":{"short":"Supporting evidence","type":"BackboneElement","array":true,"index":11,"elements":{"code":{"short":"Manifestation","type":"CodeableConcept","binding":{"strength":"example","valueSet":"http://hl7.org/fhir/ValueSet/manifestation-or-symptom","bindingName":"DetectedIssueEvidenceCode"},"array":true,"index":12},"detail":{"short":"Supporting information","type":"Reference","refers":["http://hl7.org/fhir/StructureDefinition/Resource"],"array":true,"index":13}}},"detail":{"short":"Description and context","type":"string","index":14},"reference":{"short":"Authority for issue","type":"uri","index":15},"mitigation":{"short":"Step taken to address","type":"BackboneElement","array":true,"index":16,"elements":{"action":{"short":"What mitigation?","type":"CodeableConcept","binding":{"strength":"preferred","valueSet":"http://hl7.org/fhir/ValueSet/detectedissue-mitigation-action","bindingName":"DetectedIssueMitigationAction"},"index":17},"date":{"short":"Date committed","type":"dateTime","index":18},"author":{"short":"Who is committing?","type":"Reference","refers":["http://hl7.org/fhir/StructureDefinition/Practitioner","http://hl7.org/fhir/StructureDefinition/PractitionerRole"],"index":19}},"required":["action"]}},"required":["status"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"implantStatus","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/device-implantStatus","version":"4.0.1","description":"Codes to represent the functional status of a device implanted in a patient. Both overall device status and an implant status need to be considered. The implant status should only be used when the [device status](device-definitions.html#Device.status) is \`active \`.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/device-implantStatus"},"index":1},"value":{"choices":["valueCode"],"index":3},"valueCode":{"type":"code","choiceOf":"value","index":4}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} @@ -939,7 +939,7 @@ exports[`IntrospectionWriter - Fhir Schema Output Check all introspection data i {"name":"EffectEvidenceSynthesis","type":"EffectEvidenceSynthesis","kind":"resource","class":"resource","url":"http://hl7.org/fhir/StructureDefinition/EffectEvidenceSynthesis","version":"4.0.1","description":"The EffectEvidenceSynthesis resource describes the difference in an outcome between exposures states in a population where the effect estimate is derived from a combination of research studies.","derivation":"specialization","base":"http://hl7.org/fhir/StructureDefinition/DomainResource","elements":{"url":{"short":"Canonical identifier for this effect evidence synthesis, represented as a URI (globally unique)","type":"uri","isSummary":true,"index":0},"identifier":{"short":"Additional identifier for the effect evidence synthesis","type":"Identifier","isSummary":true,"array":true,"index":1},"version":{"short":"Business version of the effect evidence synthesis","type":"string","isSummary":true,"index":2},"name":{"short":"Name for this effect evidence synthesis (computer friendly)","type":"string","isSummary":true,"index":3},"title":{"short":"Name for this effect evidence synthesis (human friendly)","type":"string","isSummary":true,"index":4},"status":{"short":"draft | active | retired | unknown","type":"code","isModifier":true,"isModifierReason":"This is labeled as \\"Is Modifier\\" because applications should not use a retired {{title}} without due consideration","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/publication-status|4.0.1","bindingName":"PublicationStatus"},"index":5},"date":{"short":"Date last changed","type":"dateTime","isSummary":true,"index":6},"publisher":{"short":"Name of the publisher (organization or individual)","type":"string","isSummary":true,"index":7},"contact":{"short":"Contact details for the publisher","type":"ContactDetail","isSummary":true,"array":true,"index":8},"description":{"short":"Natural language description of the effect evidence synthesis","type":"markdown","isSummary":true,"index":9},"note":{"short":"Used for footnotes or explanatory notes","type":"Annotation","array":true,"index":10},"useContext":{"short":"The context that the content is intended to support","type":"UsageContext","isSummary":true,"array":true,"index":11},"jurisdiction":{"short":"Intended jurisdiction for effect evidence synthesis (if applicable)","type":"CodeableConcept","isSummary":true,"binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/jurisdiction","bindingName":"Jurisdiction"},"array":true,"index":12},"copyright":{"short":"Use and/or publishing restrictions","type":"markdown","index":13},"approvalDate":{"short":"When the effect evidence synthesis was approved by publisher","type":"date","index":14},"lastReviewDate":{"short":"When the effect evidence synthesis was last reviewed","type":"date","index":15},"effectivePeriod":{"short":"When the effect evidence synthesis is expected to be used","type":"Period","isSummary":true,"index":16},"topic":{"short":"The category of the EffectEvidenceSynthesis, such as Education, Treatment, Assessment, etc.","type":"CodeableConcept","binding":{"strength":"example","valueSet":"http://hl7.org/fhir/ValueSet/definition-topic","bindingName":"DefinitionTopic"},"array":true,"index":17},"author":{"short":"Who authored the content","type":"ContactDetail","array":true,"index":18},"editor":{"short":"Who edited the content","type":"ContactDetail","array":true,"index":19},"reviewer":{"short":"Who reviewed the content","type":"ContactDetail","array":true,"index":20},"endorser":{"short":"Who endorsed the content","type":"ContactDetail","array":true,"index":21},"relatedArtifact":{"short":"Additional documentation, citations, etc.","type":"RelatedArtifact","array":true,"index":22},"synthesisType":{"short":"Type of synthesis","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/synthesis-type","bindingName":"SynthesisType"},"index":23},"studyType":{"short":"Type of study","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/study-type","bindingName":"StudyType"},"index":24},"population":{"short":"What population?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/EvidenceVariable"],"index":25},"exposure":{"short":"What exposure?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/EvidenceVariable"],"index":26},"exposureAlternative":{"short":"What comparison exposure?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/EvidenceVariable"],"index":27},"outcome":{"short":"What outcome?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/EvidenceVariable"],"index":28},"sampleSize":{"short":"What sample size was involved?","type":"BackboneElement","index":29,"elements":{"description":{"short":"Description of sample size","type":"string","index":30},"numberOfStudies":{"short":"How many studies?","type":"integer","index":31},"numberOfParticipants":{"short":"How many participants?","type":"integer","index":32}}},"resultsByExposure":{"short":"What was the result per exposure?","type":"BackboneElement","array":true,"index":33,"elements":{"description":{"short":"Description of results by exposure","type":"string","index":34},"exposureState":{"short":"exposure | exposure-alternative","type":"code","binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/exposure-state|4.0.1","bindingName":"ExposureState"},"index":35},"variantState":{"short":"Variant exposure states","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/evidence-variant-state","bindingName":"EvidenceVariantState"},"index":36},"riskEvidenceSynthesis":{"short":"Risk evidence synthesis","type":"Reference","refers":["http://hl7.org/fhir/StructureDefinition/RiskEvidenceSynthesis"],"index":37}},"required":["riskEvidenceSynthesis"]},"effectEstimate":{"short":"What was the estimated effect","type":"BackboneElement","isSummary":true,"array":true,"index":38,"elements":{"description":{"short":"Description of effect estimate","type":"string","index":39},"type":{"short":"Type of efffect estimate","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/effect-estimate-type","bindingName":"EffectEstimateType"},"index":40},"variantState":{"short":"Variant exposure states","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/evidence-variant-state","bindingName":"EvidenceVariantState"},"index":41},"value":{"short":"Point estimate","type":"decimal","index":42},"unitOfMeasure":{"short":"What unit is the outcome described in?","type":"CodeableConcept","binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/ucum-units|4.0.1","bindingName":"UCUMUnits"},"index":43},"precisionEstimate":{"short":"How precise the estimate is","type":"BackboneElement","array":true,"index":44,"elements":{"type":{"short":"Type of precision estimate","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/precision-estimate-type","bindingName":"PrecisionEstimateType"},"index":45},"level":{"short":"Level of confidence interval","type":"decimal","index":46},"from":{"short":"Lower bound","type":"decimal","index":47},"to":{"short":"Upper bound","type":"decimal","index":48}}}}},"certainty":{"short":"How certain is the effect","type":"BackboneElement","array":true,"index":49,"elements":{"rating":{"short":"Certainty rating","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/evidence-quality","bindingName":"QualityOfEvidenceRating"},"array":true,"index":50},"note":{"short":"Used for footnotes or explanatory notes","type":"Annotation","array":true,"index":51},"certaintySubcomponent":{"short":"A component that contributes to the overall certainty","type":"BackboneElement","array":true,"index":52,"elements":{"type":{"short":"Type of subcomponent of certainty rating","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/certainty-subcomponent-type","bindingName":"CertaintySubcomponentType"},"index":53},"rating":{"short":"Subcomponent certainty rating","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/certainty-subcomponent-rating","bindingName":"CertaintySubcomponentRating"},"array":true,"index":54},"note":{"short":"Used for footnotes or explanatory notes","type":"Annotation","array":true,"index":55}}}}}},"required":["exposure","exposureAlternative","outcome","population","status"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"EHRS FM Record Lifecycle Event - Audit Event","type":"AuditEvent","kind":"resource","class":"profile","url":"http://hl7.org/fhir/StructureDefinition/ehrsrle-auditevent","version":"4.0.1","description":"Defines the elements to be supported within the AuditEvent resource in order to conform with the Electronic Health Record System Functional Model Record Lifecycle Event standard","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/AuditEvent","elements":{"type":{"mustSupport":true,"index":0},"subtype":{"mustSupport":true,"index":1},"action":{"mustSupport":true,"index":2},"recorded":{"mustSupport":true,"index":3},"purposeOfEvent":{"mustSupport":true,"index":4},"agent":{"mustSupport":true,"index":5,"elements":{"role":{"mustSupport":true,"index":6},"who":{"mustSupport":true,"index":7},"requestor":{"mustSupport":true,"index":8},"location":{"mustSupport":true,"index":9},"policy":{"mustSupport":true,"index":10},"network":{"mustSupport":true,"index":11,"elements":{"address":{"mustSupport":true,"index":12},"type":{"mustSupport":true,"index":13}}},"purposeOfUse":{"mustSupport":true,"index":14}}},"source":{"mustSupport":true,"index":15,"elements":{"site":{"mustSupport":true,"index":16},"observer":{"mustSupport":true,"index":17},"type":{"mustSupport":true,"index":18}}},"entity":{"mustSupport":true,"index":19,"elements":{"what":{"mustSupport":true,"index":20},"type":{"mustSupport":true,"index":21},"role":{"mustSupport":true,"index":22},"lifecycle":{"mustSupport":true,"index":23},"securityLabel":{"mustSupport":true,"index":24}}}},"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"EHRS FM Record Lifecycle Event - Provenance","type":"Provenance","kind":"resource","class":"profile","url":"http://hl7.org/fhir/StructureDefinition/ehrsrle-provenance","version":"4.0.1","description":"Defines the elements to be supported within the Provenance resource in order to conform with the Electronic Health Record System Functional Model Record Lifecycle Event standard","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Provenance","elements":{"target":{"mustSupport":true,"index":0},"recorded":{"mustSupport":true,"index":2},"policy":{"mustSupport":true,"index":3},"location":{"mustSupport":true,"index":4},"reason":{"mustSupport":true,"index":5},"activity":{"mustSupport":true,"index":6},"agent":{"mustSupport":true,"index":7,"elements":{"who":{"mustSupport":true,"index":8},"onBehalfOf":{"mustSupport":true,"index":9}}},"signature":{"mustSupport":true,"index":10}},"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} -{"name":"Element","type":"Element","kind":"complex-type","class":"complex-type","url":"http://hl7.org/fhir/StructureDefinition/Element","version":"4.0.1","description":"Base StructureDefinition for Element Type: Base definition for all elements in a resource.","abstract":true,"elements":{"id":{"representation":["xmlAttr"],"short":"Unique id for inter-element referencing","type":"string","index":0},"extension":{"short":"Additional content defined by implementations","type":"Extension","array":true,"index":1}},"extensions":{},"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} +{"name":"Element","type":"Element","kind":"complex-type","class":"complex-type","url":"http://hl7.org/fhir/StructureDefinition/Element","version":"4.0.1","description":"Base StructureDefinition for Element Type: Base definition for all elements in a resource.","abstract":true,"elements":{"id":{"representation":["xmlAttr"],"short":"Unique id for inter-element referencing","type":"string","index":0},"extension":{"short":"Additional content defined by implementations","type":"Extension","array":true,"index":1}},"extensions":{},"derivation":"specialization","package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"allowedUnits","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/elementdefinition-allowedUnits","version":"4.0.1","description":"Identifies the units of measure in which the element should be captured or expressed.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/elementdefinition-allowedUnits"},"index":1},"value":{"choices":["valueCodeableConcept","valueCanonical"],"index":3},"valueCodeableConcept":{"type":"CodeableConcept","choiceOf":"value","index":4},"valueCanonical":{"type":"canonical","choiceOf":"value","index":5}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"bestpractice-explanation","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/elementdefinition-bestpractice-explanation","version":"4.0.1","description":"Explains why an invariant is labelled as a best practice invariant.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/elementdefinition-bestpractice-explanation"},"index":1},"value":{"choices":["valueMarkdown"],"index":3},"valueMarkdown":{"type":"markdown","choiceOf":"value","index":4}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"bestpractice","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/elementdefinition-bestpractice","version":"4.0.1","description":"Mark that an invariant represents 'best practice' rule - a rule that implementers may choose to enforce at error level in some or all circumstances.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/elementdefinition-bestpractice"},"index":1},"value":{"choices":["valueBoolean","valueCodeableConcept"],"index":3},"valueBoolean":{"type":"boolean","choiceOf":"value","index":4},"valueCodeableConcept":{"type":"CodeableConcept","choiceOf":"value","index":5}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} @@ -972,7 +972,7 @@ exports[`IntrospectionWriter - Fhir Schema Output Check all introspection data i {"name":"partOf","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/event-partOf","version":"4.0.1","description":"A larger event of which this particular event is a component or step.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/event-partOf"},"index":1},"value":{"choices":["valueReference"],"index":3},"valueReference":{"type":"Reference","choiceOf":"value","refers":["http://hl7.org/fhir/StructureDefinition/Resource"],"index":4}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"performerFunction","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/event-performerFunction","version":"4.0.1","description":"Distinguishes the type of involvement of the performer in the event. For example, 'author', 'verifier' or 'responsible party'.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/event-performerFunction"},"index":1},"value":{"choices":["valueCodeableConcept"],"index":3},"valueCodeableConcept":{"type":"CodeableConcept","choiceOf":"value","index":4}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"statusReason","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/event-statusReason","version":"4.0.1","description":"Captures the reason for the current state of the resource.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/event-statusReason"},"index":1},"value":{"choices":["valueCodeableConcept"],"index":3},"valueCodeableConcept":{"type":"CodeableConcept","choiceOf":"value","index":4}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} -{"name":"Event","type":"Event","kind":"logical","class":"logical","url":"http://hl7.org/fhir/StructureDefinition/Event","version":"4.0.1","description":"Logical Model: A pattern to be followed by resources that represent the performance of some activity, possibly in accordance with a request or service definition.","package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} +{"name":"Event","type":"Event","kind":"logical","class":"logical","url":"http://hl7.org/fhir/StructureDefinition/Event","version":"4.0.1","description":"Logical Model: A pattern to be followed by resources that represent the performance of some activity, possibly in accordance with a request or service definition.","derivation":"specialization","package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"EventDefinition","type":"EventDefinition","kind":"resource","class":"resource","url":"http://hl7.org/fhir/StructureDefinition/EventDefinition","version":"4.0.1","description":"The EventDefinition resource provides a reusable description of when a particular event can occur.","derivation":"specialization","base":"http://hl7.org/fhir/StructureDefinition/DomainResource","elements":{"url":{"short":"Canonical identifier for this event definition, represented as a URI (globally unique)","type":"uri","isSummary":true,"index":0},"identifier":{"short":"Additional identifier for the event definition","type":"Identifier","isSummary":true,"array":true,"index":1},"version":{"short":"Business version of the event definition","type":"string","isSummary":true,"index":2},"name":{"short":"Name for this event definition (computer friendly)","type":"string","isSummary":true,"index":3},"title":{"short":"Name for this event definition (human friendly)","type":"string","isSummary":true,"index":4},"subtitle":{"short":"Subordinate title of the event definition","type":"string","index":5},"status":{"short":"draft | active | retired | unknown","type":"code","isModifier":true,"isModifierReason":"This is labeled as \\"Is Modifier\\" because applications should not use a retired {{title}} without due consideration","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/publication-status|4.0.1","bindingName":"PublicationStatus"},"index":6},"experimental":{"short":"For testing purposes, not real usage","type":"boolean","isSummary":true,"index":7},"subject":{"short":"Type of individual the event definition is focused on","meaningWhenMissing":"Patient","choices":["subjectCodeableConcept","subjectReference"],"index":9},"subjectCodeableConcept":{"short":"Type of individual the event definition is focused on","type":"CodeableConcept","meaningWhenMissing":"Patient","choiceOf":"subject","index":10},"subjectReference":{"short":"Type of individual the event definition is focused on","type":"Reference","meaningWhenMissing":"Patient","choiceOf":"subject","refers":["http://hl7.org/fhir/StructureDefinition/Group"],"index":11},"date":{"short":"Date last changed","type":"dateTime","isSummary":true,"index":12},"publisher":{"short":"Name of the publisher (organization or individual)","type":"string","isSummary":true,"index":13},"contact":{"short":"Contact details for the publisher","type":"ContactDetail","isSummary":true,"array":true,"index":14},"description":{"short":"Natural language description of the event definition","type":"markdown","index":15},"useContext":{"short":"The context that the content is intended to support","type":"UsageContext","isSummary":true,"array":true,"index":16},"jurisdiction":{"short":"Intended jurisdiction for event definition (if applicable)","type":"CodeableConcept","isSummary":true,"binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/jurisdiction","bindingName":"Jurisdiction"},"array":true,"index":17},"purpose":{"short":"Why this event definition is defined","type":"markdown","index":18},"usage":{"short":"Describes the clinical usage of the event definition","type":"string","index":19},"copyright":{"short":"Use and/or publishing restrictions","type":"markdown","index":20},"approvalDate":{"short":"When the event definition was approved by publisher","type":"date","isSummary":true,"index":21},"lastReviewDate":{"short":"When the event definition was last reviewed","type":"date","isSummary":true,"index":22},"effectivePeriod":{"short":"When the event definition is expected to be used","type":"Period","isSummary":true,"index":23},"topic":{"short":"E.g. Education, Treatment, Assessment, etc.","type":"CodeableConcept","binding":{"strength":"example","valueSet":"http://hl7.org/fhir/ValueSet/definition-topic","bindingName":"DefinitionTopic"},"array":true,"index":24},"author":{"short":"Who authored the content","type":"ContactDetail","array":true,"index":25},"editor":{"short":"Who edited the content","type":"ContactDetail","array":true,"index":26},"reviewer":{"short":"Who reviewed the content","type":"ContactDetail","array":true,"index":27},"endorser":{"short":"Who endorsed the content","type":"ContactDetail","array":true,"index":28},"relatedArtifact":{"short":"Additional documentation, citations, etc.","type":"RelatedArtifact","array":true,"index":29},"trigger":{"short":"\\"when\\" the event occurs (multiple = 'or')","type":"TriggerDefinition","isSummary":true,"array":true,"min":1,"index":30}},"required":["status","trigger"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"Evidence","type":"Evidence","kind":"resource","class":"resource","url":"http://hl7.org/fhir/StructureDefinition/Evidence","version":"4.0.1","description":"The Evidence resource describes the conditional state (population and any exposures being compared within the population) and outcome (if specified) that the knowledge (evidence, assertion, recommendation) is about.","derivation":"specialization","base":"http://hl7.org/fhir/StructureDefinition/DomainResource","elements":{"url":{"short":"Canonical identifier for this evidence, represented as a URI (globally unique)","type":"uri","isSummary":true,"index":0},"identifier":{"short":"Additional identifier for the evidence","type":"Identifier","isSummary":true,"array":true,"index":1},"version":{"short":"Business version of the evidence","type":"string","isSummary":true,"index":2},"name":{"short":"Name for this evidence (computer friendly)","type":"string","isSummary":true,"index":3},"title":{"short":"Name for this evidence (human friendly)","type":"string","isSummary":true,"index":4},"shortTitle":{"short":"Title for use in informal contexts","type":"string","index":5},"subtitle":{"short":"Subordinate title of the Evidence","type":"string","index":6},"status":{"short":"draft | active | retired | unknown","type":"code","isModifier":true,"isModifierReason":"This is labeled as \\"Is Modifier\\" because applications should not use a retired {{title}} without due consideration","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/publication-status|4.0.1","bindingName":"PublicationStatus"},"index":7},"date":{"short":"Date last changed","type":"dateTime","isSummary":true,"index":8},"publisher":{"short":"Name of the publisher (organization or individual)","type":"string","isSummary":true,"index":9},"contact":{"short":"Contact details for the publisher","type":"ContactDetail","isSummary":true,"array":true,"index":10},"description":{"short":"Natural language description of the evidence","type":"markdown","isSummary":true,"index":11},"note":{"short":"Used for footnotes or explanatory notes","type":"Annotation","array":true,"index":12},"useContext":{"short":"The context that the content is intended to support","type":"UsageContext","isSummary":true,"array":true,"index":13},"jurisdiction":{"short":"Intended jurisdiction for evidence (if applicable)","type":"CodeableConcept","isSummary":true,"binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/jurisdiction","bindingName":"Jurisdiction"},"array":true,"index":14},"copyright":{"short":"Use and/or publishing restrictions","type":"markdown","index":15},"approvalDate":{"short":"When the evidence was approved by publisher","type":"date","index":16},"lastReviewDate":{"short":"When the evidence was last reviewed","type":"date","index":17},"effectivePeriod":{"short":"When the evidence is expected to be used","type":"Period","isSummary":true,"index":18},"topic":{"short":"The category of the Evidence, such as Education, Treatment, Assessment, etc.","type":"CodeableConcept","binding":{"strength":"example","valueSet":"http://hl7.org/fhir/ValueSet/definition-topic","bindingName":"DefinitionTopic"},"array":true,"index":19},"author":{"short":"Who authored the content","type":"ContactDetail","array":true,"index":20},"editor":{"short":"Who edited the content","type":"ContactDetail","array":true,"index":21},"reviewer":{"short":"Who reviewed the content","type":"ContactDetail","array":true,"index":22},"endorser":{"short":"Who endorsed the content","type":"ContactDetail","array":true,"index":23},"relatedArtifact":{"short":"Additional documentation, citations, etc.","type":"RelatedArtifact","array":true,"index":24},"exposureBackground":{"short":"What population?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/EvidenceVariable"],"index":25},"exposureVariant":{"short":"What exposure?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/EvidenceVariable"],"array":true,"index":26},"outcome":{"short":"What outcome?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/EvidenceVariable"],"array":true,"index":27}},"required":["exposureBackground","status"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"EvidenceVariable","type":"EvidenceVariable","kind":"resource","class":"resource","url":"http://hl7.org/fhir/StructureDefinition/EvidenceVariable","version":"4.0.1","description":"The EvidenceVariable resource describes a \\"PICO\\" element that knowledge (evidence, assertion, recommendation) is about.","derivation":"specialization","base":"http://hl7.org/fhir/StructureDefinition/DomainResource","elements":{"url":{"short":"Canonical identifier for this evidence variable, represented as a URI (globally unique)","type":"uri","isSummary":true,"index":0},"identifier":{"short":"Additional identifier for the evidence variable","type":"Identifier","isSummary":true,"array":true,"index":1},"version":{"short":"Business version of the evidence variable","type":"string","isSummary":true,"index":2},"name":{"short":"Name for this evidence variable (computer friendly)","type":"string","isSummary":true,"index":3},"title":{"short":"Name for this evidence variable (human friendly)","type":"string","isSummary":true,"index":4},"shortTitle":{"short":"Title for use in informal contexts","type":"string","isSummary":true,"index":5},"subtitle":{"short":"Subordinate title of the EvidenceVariable","type":"string","index":6},"status":{"short":"draft | active | retired | unknown","type":"code","isModifier":true,"isModifierReason":"This is labeled as \\"Is Modifier\\" because applications should not use a retired {{title}} without due consideration","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/publication-status|4.0.1","bindingName":"PublicationStatus"},"index":7},"date":{"short":"Date last changed","type":"dateTime","isSummary":true,"index":8},"publisher":{"short":"Name of the publisher (organization or individual)","type":"string","isSummary":true,"index":9},"contact":{"short":"Contact details for the publisher","type":"ContactDetail","isSummary":true,"array":true,"index":10},"description":{"short":"Natural language description of the evidence variable","type":"markdown","isSummary":true,"index":11},"note":{"short":"Used for footnotes or explanatory notes","type":"Annotation","array":true,"index":12},"useContext":{"short":"The context that the content is intended to support","type":"UsageContext","isSummary":true,"array":true,"index":13},"jurisdiction":{"short":"Intended jurisdiction for evidence variable (if applicable)","type":"CodeableConcept","isSummary":true,"binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/jurisdiction","bindingName":"Jurisdiction"},"array":true,"index":14},"copyright":{"short":"Use and/or publishing restrictions","type":"markdown","index":15},"approvalDate":{"short":"When the evidence variable was approved by publisher","type":"date","index":16},"lastReviewDate":{"short":"When the evidence variable was last reviewed","type":"date","index":17},"effectivePeriod":{"short":"When the evidence variable is expected to be used","type":"Period","isSummary":true,"index":18},"topic":{"short":"The category of the EvidenceVariable, such as Education, Treatment, Assessment, etc.","type":"CodeableConcept","binding":{"strength":"example","valueSet":"http://hl7.org/fhir/ValueSet/definition-topic","bindingName":"DefinitionTopic"},"array":true,"index":19},"author":{"short":"Who authored the content","type":"ContactDetail","array":true,"index":20},"editor":{"short":"Who edited the content","type":"ContactDetail","array":true,"index":21},"reviewer":{"short":"Who reviewed the content","type":"ContactDetail","array":true,"index":22},"endorser":{"short":"Who endorsed the content","type":"ContactDetail","array":true,"index":23},"relatedArtifact":{"short":"Additional documentation, citations, etc.","type":"RelatedArtifact","array":true,"index":24},"type":{"short":"dichotomous | continuous | descriptive","type":"code","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/variable-type|4.0.1","bindingName":"EvidenceVariableType"},"index":25},"characteristic":{"short":"What defines the members of the evidence element","type":"BackboneElement","isSummary":true,"array":true,"min":1,"index":26,"elements":{"description":{"short":"Natural language description of the characteristic","type":"string","index":27},"definition":{"short":"What code or expression defines members?","isSummary":true,"choices":["definitionReference","definitionCanonical","definitionCodeableConcept","definitionExpression","definitionDataRequirement","definitionTriggerDefinition"],"index":29},"definitionReference":{"short":"What code or expression defines members?","type":"Reference","isSummary":true,"choiceOf":"definition","refers":["http://hl7.org/fhir/StructureDefinition/Group"],"index":30},"definitionCanonical":{"short":"What code or expression defines members?","type":"canonical","isSummary":true,"choiceOf":"definition","index":31},"definitionCodeableConcept":{"short":"What code or expression defines members?","type":"CodeableConcept","isSummary":true,"choiceOf":"definition","index":32},"definitionExpression":{"short":"What code or expression defines members?","type":"Expression","isSummary":true,"choiceOf":"definition","index":33},"definitionDataRequirement":{"short":"What code or expression defines members?","type":"DataRequirement","isSummary":true,"choiceOf":"definition","index":34},"definitionTriggerDefinition":{"short":"What code or expression defines members?","type":"TriggerDefinition","isSummary":true,"choiceOf":"definition","index":35},"usageContext":{"short":"What code/value pairs define members?","type":"UsageContext","array":true,"index":36},"exclude":{"short":"Whether the characteristic includes or excludes members","type":"boolean","meaningWhenMissing":"False","index":37},"participantEffective":{"short":"What time period do participants cover","choices":["participantEffectiveDateTime","participantEffectivePeriod","participantEffectiveDuration","participantEffectiveTiming"],"index":39},"participantEffectiveDateTime":{"short":"What time period do participants cover","type":"dateTime","choiceOf":"participantEffective","index":40},"participantEffectivePeriod":{"short":"What time period do participants cover","type":"Period","choiceOf":"participantEffective","index":41},"participantEffectiveDuration":{"short":"What time period do participants cover","type":"Duration","choiceOf":"participantEffective","index":42},"participantEffectiveTiming":{"short":"What time period do participants cover","type":"Timing","choiceOf":"participantEffective","index":43},"timeFromStart":{"short":"Observation time from study start","type":"Duration","index":44},"groupMeasure":{"short":"mean | median | mean-of-mean | mean-of-median | median-of-mean | median-of-median","type":"code","binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/group-measure|4.0.1","bindingName":"GroupMeasure"},"index":45}},"required":["definition"]}},"required":["characteristic","status"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} @@ -991,7 +991,7 @@ exports[`IntrospectionWriter - Fhir Schema Output Check all introspection data i {"name":"severity","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/familymemberhistory-severity","version":"4.0.1","description":"A qualification of the seriousness or impact on health of the family member condition.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/familymemberhistory-severity"},"index":1},"value":{"choices":["valueCodeableConcept"],"index":3},"valueCodeableConcept":{"type":"CodeableConcept","choiceOf":"value","index":4}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"type","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/familymemberhistory-type","version":"4.0.1","description":"Purpose of the family member history or why it was created, such as when family member history is targeted for cardiovascular health, mental health, or genetic counseling.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/familymemberhistory-type"},"index":1},"value":{"choices":["valueCodeableConcept"],"index":3},"valueCodeableConcept":{"type":"CodeableConcept","choiceOf":"value","index":4}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"FamilyMemberHistory","type":"FamilyMemberHistory","kind":"resource","class":"resource","url":"http://hl7.org/fhir/StructureDefinition/FamilyMemberHistory","version":"4.0.1","description":"Significant health conditions for a person related to the patient relevant in the context of care for the patient.","derivation":"specialization","base":"http://hl7.org/fhir/StructureDefinition/DomainResource","elements":{"identifier":{"short":"External Id(s) for this record","type":"Identifier","isSummary":true,"array":true,"index":0},"instantiatesCanonical":{"short":"Instantiates FHIR protocol or definition","type":"canonical","isSummary":true,"array":true,"index":1},"instantiatesUri":{"short":"Instantiates external protocol or definition","type":"uri","isSummary":true,"array":true,"index":2},"status":{"short":"partial | completed | entered-in-error | health-unknown","type":"code","isModifier":true,"isModifierReason":"This element is labelled as a modifier because it is a status element that contains status entered-in-error which means that the resource should not be treated as valid","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/history-status|4.0.1","bindingName":"FamilyHistoryStatus"},"index":3},"dataAbsentReason":{"short":"subject-unknown | withheld | unable-to-obtain | deferred","type":"CodeableConcept","isSummary":true,"binding":{"strength":"example","valueSet":"http://hl7.org/fhir/ValueSet/history-absent-reason","bindingName":"FamilyHistoryAbsentReason"},"index":4},"patient":{"short":"Patient history is about","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/Patient"],"index":5},"date":{"short":"When history was recorded or last updated","type":"dateTime","isSummary":true,"index":6},"name":{"short":"The family member described","type":"string","isSummary":true,"index":7},"relationship":{"short":"Relationship to the subject","type":"CodeableConcept","isSummary":true,"binding":{"strength":"example","valueSet":"http://terminology.hl7.org/ValueSet/v3-FamilyMember","bindingName":"FamilialRelationship"},"index":8},"sex":{"short":"male | female | other | unknown","type":"CodeableConcept","isSummary":true,"binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/administrative-gender","bindingName":"Sex"},"index":9},"born":{"short":"(approximate) date of birth","choices":["bornPeriod","bornDate","bornString"],"index":11},"bornPeriod":{"short":"(approximate) date of birth","type":"Period","choiceOf":"born","index":12},"bornDate":{"short":"(approximate) date of birth","type":"date","choiceOf":"born","index":13},"bornString":{"short":"(approximate) date of birth","type":"string","choiceOf":"born","index":14},"age":{"short":"(approximate) age","isSummary":true,"choices":["ageAge","ageRange","ageString"],"index":16},"ageAge":{"short":"(approximate) age","type":"Age","isSummary":true,"choiceOf":"age","index":17},"ageRange":{"short":"(approximate) age","type":"Range","isSummary":true,"choiceOf":"age","index":18},"ageString":{"short":"(approximate) age","type":"string","isSummary":true,"choiceOf":"age","index":19},"estimatedAge":{"short":"Age is estimated?","type":"boolean","meaningWhenMissing":"It is unknown whether the age is an estimate or not","isSummary":true,"index":20},"deceased":{"short":"Dead? How old/when?","isSummary":true,"choices":["deceasedBoolean","deceasedAge","deceasedRange","deceasedDate","deceasedString"],"index":22},"deceasedBoolean":{"short":"Dead? How old/when?","type":"boolean","isSummary":true,"choiceOf":"deceased","index":23},"deceasedAge":{"short":"Dead? How old/when?","type":"Age","isSummary":true,"choiceOf":"deceased","index":24},"deceasedRange":{"short":"Dead? How old/when?","type":"Range","isSummary":true,"choiceOf":"deceased","index":25},"deceasedDate":{"short":"Dead? How old/when?","type":"date","isSummary":true,"choiceOf":"deceased","index":26},"deceasedString":{"short":"Dead? How old/when?","type":"string","isSummary":true,"choiceOf":"deceased","index":27},"reasonCode":{"short":"Why was family member history performed?","type":"CodeableConcept","isSummary":true,"binding":{"strength":"example","valueSet":"http://hl7.org/fhir/ValueSet/clinical-findings","bindingName":"FamilyHistoryReason"},"array":true,"index":28},"reasonReference":{"short":"Why was family member history performed?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/AllergyIntolerance","http://hl7.org/fhir/StructureDefinition/Condition","http://hl7.org/fhir/StructureDefinition/DiagnosticReport","http://hl7.org/fhir/StructureDefinition/DocumentReference","http://hl7.org/fhir/StructureDefinition/Observation","http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse"],"array":true,"index":29},"note":{"short":"General note about related person","type":"Annotation","array":true,"index":30},"condition":{"short":"Condition that the related person had","type":"BackboneElement","array":true,"index":31,"elements":{"code":{"short":"Condition suffered by relation","type":"CodeableConcept","binding":{"strength":"example","valueSet":"http://hl7.org/fhir/ValueSet/condition-code","bindingName":"ConditionCode"},"index":32},"outcome":{"short":"deceased | permanent disability | etc.","type":"CodeableConcept","binding":{"strength":"example","valueSet":"http://hl7.org/fhir/ValueSet/condition-outcome","bindingName":"ConditionOutcome"},"index":33},"contributedToDeath":{"short":"Whether the condition contributed to the cause of death","type":"boolean","index":34},"onset":{"short":"When condition first manifested","choices":["onsetAge","onsetRange","onsetPeriod","onsetString"],"index":36},"onsetAge":{"short":"When condition first manifested","type":"Age","choiceOf":"onset","index":37},"onsetRange":{"short":"When condition first manifested","type":"Range","choiceOf":"onset","index":38},"onsetPeriod":{"short":"When condition first manifested","type":"Period","choiceOf":"onset","index":39},"onsetString":{"short":"When condition first manifested","type":"string","choiceOf":"onset","index":40},"note":{"short":"Extra information about condition","type":"Annotation","array":true,"index":41}},"required":["code"]}},"required":["patient","relationship","status"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} -{"name":"FiveWs","type":"FiveWs","kind":"logical","class":"logical","url":"http://hl7.org/fhir/StructureDefinition/FiveWs","version":"4.0.1","description":"Logical Model: Who What When Where Why - Common pattern for all resources that deals with attribution.","package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} +{"name":"FiveWs","type":"FiveWs","kind":"logical","class":"logical","url":"http://hl7.org/fhir/StructureDefinition/FiveWs","version":"4.0.1","description":"Logical Model: Who What When Where Why - Common pattern for all resources that deals with attribution.","derivation":"specialization","package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"detail","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/flag-detail","version":"4.0.1","description":"Points to the Observation, AllergyIntolerance or other record that provides additional supporting information about this flag.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/flag-detail"},"index":1},"value":{"choices":["valueReference"],"index":3},"valueReference":{"type":"Reference","choiceOf":"value","refers":["http://hl7.org/fhir/StructureDefinition/Resource"],"index":4}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"priority","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/flag-priority","version":"4.0.1","description":"A code that identifies the priority of the alert, for example the Alert Priority flags column in IHE PCD TF 2 Table B.8-4.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/flag-priority"},"index":1},"value":{"choices":["valueCodeableConcept"],"index":3},"valueCodeableConcept":{"type":"CodeableConcept","choiceOf":"value","index":4}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"Flag","type":"Flag","kind":"resource","class":"resource","url":"http://hl7.org/fhir/StructureDefinition/Flag","version":"4.0.1","description":"Prospective warnings of potential issues when providing care to the patient.","derivation":"specialization","base":"http://hl7.org/fhir/StructureDefinition/DomainResource","elements":{"identifier":{"short":"Business identifier","type":"Identifier","isSummary":true,"array":true,"index":0},"status":{"short":"active | inactive | entered-in-error","type":"code","isModifier":true,"isModifierReason":"This element is labelled as a modifier because it is a status element that contains status entered-in-error which means that the resource should not be treated as valid","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/flag-status|4.0.1","bindingName":"FlagStatus"},"index":1},"category":{"short":"Clinical, administrative, etc.","type":"CodeableConcept","isSummary":true,"binding":{"strength":"example","valueSet":"http://hl7.org/fhir/ValueSet/flag-category","bindingName":"FlagCategory"},"array":true,"index":2},"code":{"short":"Coded or textual message to display to user","type":"CodeableConcept","isSummary":true,"binding":{"strength":"example","valueSet":"http://hl7.org/fhir/ValueSet/flag-code","bindingName":"FlagCode"},"index":3},"subject":{"short":"Who/What is flag about?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/Group","http://hl7.org/fhir/StructureDefinition/Location","http://hl7.org/fhir/StructureDefinition/Medication","http://hl7.org/fhir/StructureDefinition/Organization","http://hl7.org/fhir/StructureDefinition/Patient","http://hl7.org/fhir/StructureDefinition/PlanDefinition","http://hl7.org/fhir/StructureDefinition/Practitioner","http://hl7.org/fhir/StructureDefinition/Procedure"],"index":4},"period":{"short":"Time period when flag is active","type":"Period","isSummary":true,"index":5},"encounter":{"short":"Alert relevant during encounter","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/Encounter"],"index":6},"author":{"short":"Flag creator","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/Device","http://hl7.org/fhir/StructureDefinition/Organization","http://hl7.org/fhir/StructureDefinition/Patient","http://hl7.org/fhir/StructureDefinition/Practitioner","http://hl7.org/fhir/StructureDefinition/PractitionerRole"],"index":7}},"required":["code","status","subject"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} @@ -1263,7 +1263,7 @@ exports[`IntrospectionWriter - Fhir Schema Output Check all introspection data i {"name":"relevantHistory","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/request-relevantHistory","version":"4.0.1","description":"Links to Provenance records for past versions of this resource or fulfilling request or event resources that identify key state transitions or updates that are likely to be relevant to a user looking at the current version of the resource.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/request-relevantHistory"},"index":1},"value":{"choices":["valueReference"],"index":3},"valueReference":{"type":"Reference","choiceOf":"value","refers":["http://hl7.org/fhir/StructureDefinition/Provenance"],"index":4}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"replaces","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/request-replaces","version":"4.0.1","description":"Completed or terminated request(s) whose function is taken by this new request.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/request-replaces"},"index":1},"value":{"choices":["valueReference"],"index":3},"valueReference":{"type":"Reference","choiceOf":"value","refers":["http://hl7.org/fhir/StructureDefinition/Resource"],"index":4}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"statusReason","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/request-statusReason","version":"4.0.1","description":"Captures the reason for the current state of the resource.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/request-statusReason"},"index":1},"value":{"choices":["valueCodeableConcept"],"index":3},"valueCodeableConcept":{"type":"CodeableConcept","choiceOf":"value","index":4}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} -{"name":"Request","type":"Request","kind":"logical","class":"logical","url":"http://hl7.org/fhir/StructureDefinition/Request","version":"4.0.1","description":"Logical Model: A pattern to be followed by resources that represent a specific proposal, plan and/or order for some sort of action or service.","package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} +{"name":"Request","type":"Request","kind":"logical","class":"logical","url":"http://hl7.org/fhir/StructureDefinition/Request","version":"4.0.1","description":"Logical Model: A pattern to be followed by resources that represent a specific proposal, plan and/or order for some sort of action or service.","derivation":"specialization","package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"RequestGroup","type":"RequestGroup","kind":"resource","class":"resource","url":"http://hl7.org/fhir/StructureDefinition/RequestGroup","version":"4.0.1","description":"A group of related requests that can be used to capture intended activities that have inter-dependencies such as \\"give this medication after that one\\".","derivation":"specialization","base":"http://hl7.org/fhir/StructureDefinition/DomainResource","elements":{"identifier":{"short":"Business identifier","type":"Identifier","isSummary":true,"array":true,"index":0},"instantiatesCanonical":{"short":"Instantiates FHIR protocol or definition","type":"canonical","isSummary":true,"array":true,"index":1},"instantiatesUri":{"short":"Instantiates external protocol or definition","type":"uri","isSummary":true,"array":true,"index":2},"basedOn":{"short":"Fulfills plan, proposal, or order","type":"Reference","refers":["http://hl7.org/fhir/StructureDefinition/Resource"],"array":true,"index":3},"replaces":{"short":"Request(s) replaced by this request","type":"Reference","refers":["http://hl7.org/fhir/StructureDefinition/Resource"],"array":true,"index":4},"groupIdentifier":{"short":"Composite request this is part of","type":"Identifier","isSummary":true,"index":5},"status":{"short":"draft | active | on-hold | revoked | completed | entered-in-error | unknown","type":"code","isModifier":true,"isModifierReason":"This element is labeled as a modifier because it is a status element that contains status entered-in-error which means that the resource should not be treated as valid","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/request-status|4.0.1","bindingName":"RequestStatus"},"index":6},"intent":{"short":"proposal | plan | directive | order | original-order | reflex-order | filler-order | instance-order | option","type":"code","isModifier":true,"isModifierReason":"This element changes the interpretation of all descriptive attributes. For example \\"the time the request is recommended to occur\\" vs. \\"the time the request is authorized to occur\\" or \\"who is recommended to perform the request\\" vs. \\"who is authorized to perform the request","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/request-intent|4.0.1","bindingName":"RequestIntent"},"index":7},"priority":{"short":"routine | urgent | asap | stat","type":"code","meaningWhenMissing":"If missing, this request should be performed with normal priority","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/request-priority|4.0.1","bindingName":"RequestPriority"},"index":8},"code":{"short":"What's being requested/ordered","type":"CodeableConcept","isSummary":true,"index":9},"subject":{"short":"Who the request group is about","type":"Reference","refers":["http://hl7.org/fhir/StructureDefinition/Group","http://hl7.org/fhir/StructureDefinition/Patient"],"index":10},"encounter":{"short":"Created as part of","type":"Reference","refers":["http://hl7.org/fhir/StructureDefinition/Encounter"],"index":11},"authoredOn":{"short":"When the request group was authored","type":"dateTime","index":12},"author":{"short":"Device or practitioner that authored the request group","type":"Reference","refers":["http://hl7.org/fhir/StructureDefinition/Device","http://hl7.org/fhir/StructureDefinition/Practitioner","http://hl7.org/fhir/StructureDefinition/PractitionerRole"],"index":13},"reasonCode":{"short":"Why the request group is needed","type":"CodeableConcept","array":true,"index":14},"reasonReference":{"short":"Why the request group is needed","type":"Reference","refers":["http://hl7.org/fhir/StructureDefinition/Condition","http://hl7.org/fhir/StructureDefinition/DiagnosticReport","http://hl7.org/fhir/StructureDefinition/DocumentReference","http://hl7.org/fhir/StructureDefinition/Observation"],"array":true,"index":15},"note":{"short":"Additional notes about the response","type":"Annotation","array":true,"index":16},"action":{"short":"Proposed actions, if any","type":"BackboneElement","constraint":{"rqg-1":{"expression":"resource.exists() != action.exists()","human":"Must have resource or action but not both","severity":"error"}},"array":true,"index":17,"elements":{"prefix":{"short":"User-visible prefix for the action (e.g. 1. or A.)","type":"string","index":18},"title":{"short":"User-visible title","type":"string","index":19},"description":{"short":"Short description of the action","type":"string","isSummary":true,"index":20},"textEquivalent":{"short":"Static text equivalent of the action, used if the dynamic aspects cannot be interpreted by the receiving system","type":"string","isSummary":true,"index":21},"priority":{"short":"routine | urgent | asap | stat","type":"code","binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/request-priority|4.0.1","bindingName":"RequestPriority"},"index":22},"code":{"short":"Code representing the meaning of the action or sub-actions","type":"CodeableConcept","array":true,"index":23},"documentation":{"short":"Supporting documentation for the intended performer of the action","type":"RelatedArtifact","array":true,"index":24},"condition":{"short":"Whether or not the action is applicable","type":"BackboneElement","array":true,"index":25,"elements":{"kind":{"short":"applicability | start | stop","type":"code","binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/action-condition-kind|4.0.1","bindingName":"ActionConditionKind"},"index":26},"expression":{"short":"Boolean-valued expression","type":"Expression","index":27}},"required":["kind"]},"relatedAction":{"short":"Relationship to another action","type":"BackboneElement","array":true,"index":28,"elements":{"actionId":{"short":"What action this is related to","type":"id","index":29},"relationship":{"short":"before-start | before | before-end | concurrent-with-start | concurrent | concurrent-with-end | after-start | after | after-end","type":"code","binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/action-relationship-type|4.0.1","bindingName":"ActionRelationshipType"},"index":30},"offset":{"short":"Time offset for the relationship","choices":["offsetDuration","offsetRange"],"index":32},"offsetDuration":{"short":"Time offset for the relationship","type":"Duration","choiceOf":"offset","index":33},"offsetRange":{"short":"Time offset for the relationship","type":"Range","choiceOf":"offset","index":34}},"required":["actionId","relationship"]},"timing":{"short":"When the action should take place","choices":["timingDateTime","timingAge","timingPeriod","timingDuration","timingRange","timingTiming"],"index":36},"timingDateTime":{"short":"When the action should take place","type":"dateTime","choiceOf":"timing","index":37},"timingAge":{"short":"When the action should take place","type":"Age","choiceOf":"timing","index":38},"timingPeriod":{"short":"When the action should take place","type":"Period","choiceOf":"timing","index":39},"timingDuration":{"short":"When the action should take place","type":"Duration","choiceOf":"timing","index":40},"timingRange":{"short":"When the action should take place","type":"Range","choiceOf":"timing","index":41},"timingTiming":{"short":"When the action should take place","type":"Timing","choiceOf":"timing","index":42},"participant":{"short":"Who should perform the action","type":"Reference","refers":["http://hl7.org/fhir/StructureDefinition/Device","http://hl7.org/fhir/StructureDefinition/Patient","http://hl7.org/fhir/StructureDefinition/Practitioner","http://hl7.org/fhir/StructureDefinition/PractitionerRole","http://hl7.org/fhir/StructureDefinition/RelatedPerson"],"array":true,"index":43},"type":{"short":"create | update | remove | fire-event","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/action-type","bindingName":"ActionType"},"index":44},"groupingBehavior":{"short":"visual-group | logical-group | sentence-group","type":"code","binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/action-grouping-behavior|4.0.1","bindingName":"ActionGroupingBehavior"},"index":45},"selectionBehavior":{"short":"any | all | all-or-none | exactly-one | at-most-one | one-or-more","type":"code","binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/action-selection-behavior|4.0.1","bindingName":"ActionSelectionBehavior"},"index":46},"requiredBehavior":{"short":"must | could | must-unless-documented","type":"code","binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/action-required-behavior|4.0.1","bindingName":"ActionRequiredBehavior"},"index":47},"precheckBehavior":{"short":"yes | no","type":"code","binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/action-precheck-behavior|4.0.1","bindingName":"ActionPrecheckBehavior"},"index":48},"cardinalityBehavior":{"short":"single | multiple","type":"code","binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/action-cardinality-behavior|4.0.1","bindingName":"ActionCardinalityBehavior"},"index":49},"resource":{"short":"The target of the action","type":"Reference","refers":["http://hl7.org/fhir/StructureDefinition/Resource"],"index":50},"action":{"short":"Sub action","elementReference":["http://hl7.org/fhir/StructureDefinition/RequestGroup","elements","action"],"array":true,"index":51}}}},"required":["intent","status"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"ResearchDefinition","type":"ResearchDefinition","kind":"resource","class":"resource","url":"http://hl7.org/fhir/StructureDefinition/ResearchDefinition","version":"4.0.1","description":"The ResearchDefinition resource describes the conditional state (population and any exposures being compared within the population) and outcome (if specified) that the knowledge (evidence, assertion, recommendation) is about.","derivation":"specialization","base":"http://hl7.org/fhir/StructureDefinition/DomainResource","elements":{"url":{"short":"Canonical identifier for this research definition, represented as a URI (globally unique)","type":"uri","isSummary":true,"index":0},"identifier":{"short":"Additional identifier for the research definition","type":"Identifier","isSummary":true,"array":true,"index":1},"version":{"short":"Business version of the research definition","type":"string","isSummary":true,"index":2},"name":{"short":"Name for this research definition (computer friendly)","type":"string","isSummary":true,"index":3},"title":{"short":"Name for this research definition (human friendly)","type":"string","isSummary":true,"index":4},"shortTitle":{"short":"Title for use in informal contexts","type":"string","index":5},"subtitle":{"short":"Subordinate title of the ResearchDefinition","type":"string","index":6},"status":{"short":"draft | active | retired | unknown","type":"code","isModifier":true,"isModifierReason":"This is labeled as \\"Is Modifier\\" because applications should not use a retired {{title}} without due consideration","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/publication-status|4.0.1","bindingName":"PublicationStatus"},"index":7},"experimental":{"short":"For testing purposes, not real usage","type":"boolean","isSummary":true,"index":8},"subject":{"short":"E.g. Patient, Practitioner, RelatedPerson, Organization, Location, Device","meaningWhenMissing":"Patient","choices":["subjectCodeableConcept","subjectReference"],"index":10},"subjectCodeableConcept":{"short":"E.g. Patient, Practitioner, RelatedPerson, Organization, Location, Device","type":"CodeableConcept","meaningWhenMissing":"Patient","choiceOf":"subject","index":11},"subjectReference":{"short":"E.g. Patient, Practitioner, RelatedPerson, Organization, Location, Device","type":"Reference","meaningWhenMissing":"Patient","choiceOf":"subject","refers":["http://hl7.org/fhir/StructureDefinition/Group"],"index":12},"date":{"short":"Date last changed","type":"dateTime","isSummary":true,"index":13},"publisher":{"short":"Name of the publisher (organization or individual)","type":"string","isSummary":true,"index":14},"contact":{"short":"Contact details for the publisher","type":"ContactDetail","isSummary":true,"array":true,"index":15},"description":{"short":"Natural language description of the research definition","type":"markdown","isSummary":true,"index":16},"comment":{"short":"Used for footnotes or explanatory notes","type":"string","array":true,"index":17},"useContext":{"short":"The context that the content is intended to support","type":"UsageContext","isSummary":true,"array":true,"index":18},"jurisdiction":{"short":"Intended jurisdiction for research definition (if applicable)","type":"CodeableConcept","isSummary":true,"binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/jurisdiction","bindingName":"Jurisdiction"},"array":true,"index":19},"purpose":{"short":"Why this research definition is defined","type":"markdown","index":20},"usage":{"short":"Describes the clinical usage of the ResearchDefinition","type":"string","index":21},"copyright":{"short":"Use and/or publishing restrictions","type":"markdown","index":22},"approvalDate":{"short":"When the research definition was approved by publisher","type":"date","index":23},"lastReviewDate":{"short":"When the research definition was last reviewed","type":"date","index":24},"effectivePeriod":{"short":"When the research definition is expected to be used","type":"Period","isSummary":true,"index":25},"topic":{"short":"The category of the ResearchDefinition, such as Education, Treatment, Assessment, etc.","type":"CodeableConcept","binding":{"strength":"example","valueSet":"http://hl7.org/fhir/ValueSet/definition-topic","bindingName":"DefinitionTopic"},"array":true,"index":26},"author":{"short":"Who authored the content","type":"ContactDetail","array":true,"index":27},"editor":{"short":"Who edited the content","type":"ContactDetail","array":true,"index":28},"reviewer":{"short":"Who reviewed the content","type":"ContactDetail","array":true,"index":29},"endorser":{"short":"Who endorsed the content","type":"ContactDetail","array":true,"index":30},"relatedArtifact":{"short":"Additional documentation, citations, etc.","type":"RelatedArtifact","array":true,"index":31},"library":{"short":"Logic used by the ResearchDefinition","type":"canonical","array":true,"index":32},"population":{"short":"What population?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/ResearchElementDefinition"],"index":33},"exposure":{"short":"What exposure?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/ResearchElementDefinition"],"index":34},"exposureAlternative":{"short":"What alternative exposure state?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/ResearchElementDefinition"],"index":35},"outcome":{"short":"What outcome?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/ResearchElementDefinition"],"index":36}},"required":["population","status"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"ResearchElementDefinition","type":"ResearchElementDefinition","kind":"resource","class":"resource","url":"http://hl7.org/fhir/StructureDefinition/ResearchElementDefinition","version":"4.0.1","description":"The ResearchElementDefinition resource describes a \\"PICO\\" element that knowledge (evidence, assertion, recommendation) is about.","derivation":"specialization","base":"http://hl7.org/fhir/StructureDefinition/DomainResource","elements":{"url":{"short":"Canonical identifier for this research element definition, represented as a URI (globally unique)","type":"uri","isSummary":true,"index":0},"identifier":{"short":"Additional identifier for the research element definition","type":"Identifier","isSummary":true,"array":true,"index":1},"version":{"short":"Business version of the research element definition","type":"string","isSummary":true,"index":2},"name":{"short":"Name for this research element definition (computer friendly)","type":"string","isSummary":true,"index":3},"title":{"short":"Name for this research element definition (human friendly)","type":"string","isSummary":true,"index":4},"shortTitle":{"short":"Title for use in informal contexts","type":"string","isSummary":true,"index":5},"subtitle":{"short":"Subordinate title of the ResearchElementDefinition","type":"string","index":6},"status":{"short":"draft | active | retired | unknown","type":"code","isModifier":true,"isModifierReason":"This is labeled as \\"Is Modifier\\" because applications should not use a retired {{title}} without due consideration","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/publication-status|4.0.1","bindingName":"PublicationStatus"},"index":7},"experimental":{"short":"For testing purposes, not real usage","type":"boolean","isSummary":true,"index":8},"subject":{"short":"E.g. Patient, Practitioner, RelatedPerson, Organization, Location, Device","meaningWhenMissing":"Patient","choices":["subjectCodeableConcept","subjectReference"],"index":10},"subjectCodeableConcept":{"short":"E.g. Patient, Practitioner, RelatedPerson, Organization, Location, Device","type":"CodeableConcept","meaningWhenMissing":"Patient","choiceOf":"subject","index":11},"subjectReference":{"short":"E.g. Patient, Practitioner, RelatedPerson, Organization, Location, Device","type":"Reference","meaningWhenMissing":"Patient","choiceOf":"subject","refers":["http://hl7.org/fhir/StructureDefinition/Group"],"index":12},"date":{"short":"Date last changed","type":"dateTime","isSummary":true,"index":13},"publisher":{"short":"Name of the publisher (organization or individual)","type":"string","isSummary":true,"index":14},"contact":{"short":"Contact details for the publisher","type":"ContactDetail","isSummary":true,"array":true,"index":15},"description":{"short":"Natural language description of the research element definition","type":"markdown","isSummary":true,"index":16},"comment":{"short":"Used for footnotes or explanatory notes","type":"string","array":true,"index":17},"useContext":{"short":"The context that the content is intended to support","type":"UsageContext","isSummary":true,"array":true,"index":18},"jurisdiction":{"short":"Intended jurisdiction for research element definition (if applicable)","type":"CodeableConcept","isSummary":true,"binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/jurisdiction","bindingName":"Jurisdiction"},"array":true,"index":19},"purpose":{"short":"Why this research element definition is defined","type":"markdown","index":20},"usage":{"short":"Describes the clinical usage of the ResearchElementDefinition","type":"string","index":21},"copyright":{"short":"Use and/or publishing restrictions","type":"markdown","index":22},"approvalDate":{"short":"When the research element definition was approved by publisher","type":"date","index":23},"lastReviewDate":{"short":"When the research element definition was last reviewed","type":"date","index":24},"effectivePeriod":{"short":"When the research element definition is expected to be used","type":"Period","isSummary":true,"index":25},"topic":{"short":"The category of the ResearchElementDefinition, such as Education, Treatment, Assessment, etc.","type":"CodeableConcept","binding":{"strength":"example","valueSet":"http://hl7.org/fhir/ValueSet/definition-topic","bindingName":"DefinitionTopic"},"array":true,"index":26},"author":{"short":"Who authored the content","type":"ContactDetail","array":true,"index":27},"editor":{"short":"Who edited the content","type":"ContactDetail","array":true,"index":28},"reviewer":{"short":"Who reviewed the content","type":"ContactDetail","array":true,"index":29},"endorser":{"short":"Who endorsed the content","type":"ContactDetail","array":true,"index":30},"relatedArtifact":{"short":"Additional documentation, citations, etc.","type":"RelatedArtifact","array":true,"index":31},"library":{"short":"Logic used by the ResearchElementDefinition","type":"canonical","array":true,"index":32},"type":{"short":"population | exposure | outcome","type":"code","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/research-element-type|4.0.1","bindingName":"ResearchElementType"},"index":33},"variableType":{"short":"dichotomous | continuous | descriptive","type":"code","binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/variable-type|4.0.1","bindingName":"VariableType"},"index":34},"characteristic":{"short":"What defines the members of the research element","type":"BackboneElement","isSummary":true,"array":true,"min":1,"index":35,"elements":{"definition":{"short":"What code or expression defines members?","isSummary":true,"choices":["definitionCodeableConcept","definitionCanonical","definitionExpression","definitionDataRequirement"],"index":37},"definitionCodeableConcept":{"short":"What code or expression defines members?","type":"CodeableConcept","isSummary":true,"choiceOf":"definition","index":38},"definitionCanonical":{"short":"What code or expression defines members?","type":"canonical","isSummary":true,"choiceOf":"definition","index":39},"definitionExpression":{"short":"What code or expression defines members?","type":"Expression","isSummary":true,"choiceOf":"definition","index":40},"definitionDataRequirement":{"short":"What code or expression defines members?","type":"DataRequirement","isSummary":true,"choiceOf":"definition","index":41},"usageContext":{"short":"What code/value pairs define members?","type":"UsageContext","array":true,"index":42},"exclude":{"short":"Whether the characteristic includes or excludes members","type":"boolean","meaningWhenMissing":"False","index":43},"unitOfMeasure":{"short":"What unit is the outcome described in?","type":"CodeableConcept","binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/ucum-units|4.0.1","bindingName":"UCUMUnits"},"index":44},"studyEffectiveDescription":{"short":"What time period does the study cover","type":"string","index":45},"studyEffective":{"short":"What time period does the study cover","choices":["studyEffectiveDateTime","studyEffectivePeriod","studyEffectiveDuration","studyEffectiveTiming"],"index":47},"studyEffectiveDateTime":{"short":"What time period does the study cover","type":"dateTime","choiceOf":"studyEffective","index":48},"studyEffectivePeriod":{"short":"What time period does the study cover","type":"Period","choiceOf":"studyEffective","index":49},"studyEffectiveDuration":{"short":"What time period does the study cover","type":"Duration","choiceOf":"studyEffective","index":50},"studyEffectiveTiming":{"short":"What time period does the study cover","type":"Timing","choiceOf":"studyEffective","index":51},"studyEffectiveTimeFromStart":{"short":"Observation time from study start","type":"Duration","index":52},"studyEffectiveGroupMeasure":{"short":"mean | median | mean-of-mean | mean-of-median | median-of-mean | median-of-median","type":"code","binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/group-measure|4.0.1","bindingName":"GroupMeasure"},"index":53},"participantEffectiveDescription":{"short":"What time period do participants cover","type":"string","index":54},"participantEffective":{"short":"What time period do participants cover","choices":["participantEffectiveDateTime","participantEffectivePeriod","participantEffectiveDuration","participantEffectiveTiming"],"index":56},"participantEffectiveDateTime":{"short":"What time period do participants cover","type":"dateTime","choiceOf":"participantEffective","index":57},"participantEffectivePeriod":{"short":"What time period do participants cover","type":"Period","choiceOf":"participantEffective","index":58},"participantEffectiveDuration":{"short":"What time period do participants cover","type":"Duration","choiceOf":"participantEffective","index":59},"participantEffectiveTiming":{"short":"What time period do participants cover","type":"Timing","choiceOf":"participantEffective","index":60},"participantEffectiveTimeFromStart":{"short":"Observation time from study start","type":"Duration","index":61},"participantEffectiveGroupMeasure":{"short":"mean | median | mean-of-mean | mean-of-median | median-of-mean | median-of-median","type":"code","binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/group-measure|4.0.1","bindingName":"GroupMeasure"},"index":62}},"required":["definition"]}},"required":["characteristic","status","type"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} @@ -1273,7 +1273,7 @@ exports[`IntrospectionWriter - Fhir Schema Output Check all introspection data i {"name":"effectivePeriod","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/resource-effectivePeriod","version":"4.0.1","description":"The period during which the resource content was or is planned to be effective.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/resource-effectivePeriod"},"index":1},"value":{"choices":["valuePeriod"],"index":3},"valuePeriod":{"type":"Period","choiceOf":"value","index":4}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"lastReviewDate","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/resource-lastReviewDate","version":"4.0.1","description":"The date on which the asset content was last reviewed. Review happens periodically after that, but doesn't change the original approval date.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/resource-lastReviewDate"},"index":1},"value":{"choices":["valueDate"],"index":3},"valueDate":{"type":"date","choiceOf":"value","index":4}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"pertainsToGoal","type":"Extension","kind":"complex-type","class":"extension","url":"http://hl7.org/fhir/StructureDefinition/resource-pertainsToGoal","version":"4.0.1","description":"Indicates that the resource is related to either the measurement, achievement or progress towards the referenced goal. For example, a Procedure to exercise pertainsToGoal of losing weight.","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/Extension","extensions":{},"elements":{"extension":{"index":0},"url":{"fixed":{"type":"uri","value":"http://hl7.org/fhir/StructureDefinition/resource-pertainsToGoal"},"index":1},"value":{"choices":["valueReference"],"index":3},"valueReference":{"type":"Reference","choiceOf":"value","refers":["http://hl7.org/fhir/StructureDefinition/Goal"],"index":4}},"required":["value"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} -{"name":"Resource","type":"Resource","kind":"resource","class":"resource","url":"http://hl7.org/fhir/StructureDefinition/Resource","version":"4.0.1","description":"This is the base resource type for everything.","abstract":true,"elements":{"id":{"short":"Logical id of this artifact","type":"string","isSummary":true,"index":0},"meta":{"short":"Metadata about the resource","type":"Meta","isSummary":true,"index":1},"implicitRules":{"short":"A set of rules under which this content was created","type":"uri","isModifier":true,"isModifierReason":"This element is labeled as a modifier because the implicit rules may provide additional knowledge about the resource that modifies it's meaning or interpretation","isSummary":true,"index":2},"language":{"short":"Language of the resource content","type":"code","binding":{"strength":"preferred","valueSet":"http://hl7.org/fhir/ValueSet/languages","bindingName":"Language"},"index":3}},"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} +{"name":"Resource","type":"Resource","kind":"resource","class":"resource","url":"http://hl7.org/fhir/StructureDefinition/Resource","version":"4.0.1","description":"This is the base resource type for everything.","abstract":true,"elements":{"id":{"short":"Logical id of this artifact","type":"string","isSummary":true,"index":0},"meta":{"short":"Metadata about the resource","type":"Meta","isSummary":true,"index":1},"implicitRules":{"short":"A set of rules under which this content was created","type":"uri","isModifier":true,"isModifierReason":"This element is labeled as a modifier because the implicit rules may provide additional knowledge about the resource that modifies it's meaning or interpretation","isSummary":true,"index":2},"language":{"short":"Language of the resource content","type":"code","binding":{"strength":"preferred","valueSet":"http://hl7.org/fhir/ValueSet/languages","bindingName":"Language"},"index":3}},"derivation":"specialization","package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"observation-resprate","type":"Observation","kind":"resource","class":"profile","url":"http://hl7.org/fhir/StructureDefinition/resprate","version":"4.0.1","description":"FHIR Respiratory Rate Profile","derivation":"constraint","base":"http://hl7.org/fhir/StructureDefinition/vitalsigns","elements":{"code":{"short":"Respiratory Rate","index":0,"elements":{"coding":{"index":1,"slicing":{"discriminator":[{"type":"value","path":"code"},{"type":"value","path":"system"}],"ordered":false,"rules":"open","slices":{"RespRateCode":{"match":{"code":"9279-1","system":"http://loinc.org"},"schema":{"_required":true,"index":2,"elements":{"system":{"type":"uri","fixed":{"type":"uri","value":"http://loinc.org"},"index":3},"code":{"type":"code","fixed":{"type":"code","value":"9279-1"},"index":4}},"required":["code","system"]},"min":1,"max":1}}}}}},"valueQuantity":{"index":5,"elements":{"value":{"type":"decimal","mustSupport":true,"index":6},"unit":{"type":"string","mustSupport":true,"index":7},"system":{"type":"uri","fixed":{"type":"uri","value":"http://unitsofmeasure.org"},"mustSupport":true,"index":8},"code":{"short":"Coded responses from the common UCUM units for vital signs value set.","type":"code","fixed":{"type":"code","value":"/min"},"mustSupport":true,"index":9}},"required":["code","system","unit","value"]}},"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"RiskAssessment","type":"RiskAssessment","kind":"resource","class":"resource","url":"http://hl7.org/fhir/StructureDefinition/RiskAssessment","version":"4.0.1","description":"An assessment of the likely outcome(s) for a patient or other subject as well as the likelihood of each outcome.","derivation":"specialization","base":"http://hl7.org/fhir/StructureDefinition/DomainResource","elements":{"identifier":{"short":"Unique identifier for the assessment","type":"Identifier","isSummary":true,"array":true,"index":0},"basedOn":{"short":"Request fulfilled by this assessment","type":"Reference","refers":["http://hl7.org/fhir/StructureDefinition/Resource"],"index":1},"parent":{"short":"Part of this occurrence","type":"Reference","refers":["http://hl7.org/fhir/StructureDefinition/Resource"],"index":2},"status":{"short":"registered | preliminary | final | amended +","type":"code","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/observation-status|4.0.1","bindingName":"RiskAssessmentStatus"},"index":3},"method":{"short":"Evaluation mechanism","type":"CodeableConcept","isSummary":true,"index":4},"code":{"short":"Type of assessment","type":"CodeableConcept","isSummary":true,"index":5},"subject":{"short":"Who/what does assessment apply to?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/Group","http://hl7.org/fhir/StructureDefinition/Patient"],"index":6},"encounter":{"short":"Where was assessment performed?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/Encounter"],"index":7},"occurrence":{"short":"When was assessment made?","isSummary":true,"choices":["occurrenceDateTime","occurrencePeriod"],"index":9},"occurrenceDateTime":{"short":"When was assessment made?","type":"dateTime","isSummary":true,"choiceOf":"occurrence","index":10},"occurrencePeriod":{"short":"When was assessment made?","type":"Period","isSummary":true,"choiceOf":"occurrence","index":11},"condition":{"short":"Condition assessed","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/Condition"],"index":12},"performer":{"short":"Who did assessment?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/Device","http://hl7.org/fhir/StructureDefinition/Practitioner","http://hl7.org/fhir/StructureDefinition/PractitionerRole"],"index":13},"reasonCode":{"short":"Why the assessment was necessary?","type":"CodeableConcept","array":true,"index":14},"reasonReference":{"short":"Why the assessment was necessary?","type":"Reference","refers":["http://hl7.org/fhir/StructureDefinition/Condition","http://hl7.org/fhir/StructureDefinition/DiagnosticReport","http://hl7.org/fhir/StructureDefinition/DocumentReference","http://hl7.org/fhir/StructureDefinition/Observation"],"array":true,"index":15},"basis":{"short":"Information used in assessment","type":"Reference","refers":["http://hl7.org/fhir/StructureDefinition/Resource"],"array":true,"index":16},"prediction":{"short":"Outcome predicted","type":"BackboneElement","constraint":{"ras-2":{"expression":"probability is decimal implies (probability as decimal) <= 100","human":"Must be <= 100","severity":"error"}},"array":true,"index":17,"elements":{"outcome":{"short":"Possible outcome for the subject","type":"CodeableConcept","index":18},"probability":{"short":"Likelihood of specified outcome","constraint":{"ras-1":{"expression":"(low.empty() or ((low.code = '%') and (low.system = %ucum))) and (high.empty() or ((high.code = '%') and (high.system = %ucum)))","human":"low and high must be percentages, if present","severity":"error"}},"choices":["probabilityDecimal","probabilityRange"],"index":20},"probabilityDecimal":{"short":"Likelihood of specified outcome","type":"decimal","constraint":{"ras-1":{"expression":"(low.empty() or ((low.code = '%') and (low.system = %ucum))) and (high.empty() or ((high.code = '%') and (high.system = %ucum)))","human":"low and high must be percentages, if present","severity":"error"}},"choiceOf":"probability","index":21},"probabilityRange":{"short":"Likelihood of specified outcome","type":"Range","constraint":{"ras-1":{"expression":"(low.empty() or ((low.code = '%') and (low.system = %ucum))) and (high.empty() or ((high.code = '%') and (high.system = %ucum)))","human":"low and high must be percentages, if present","severity":"error"}},"choiceOf":"probability","index":22},"qualitativeRisk":{"short":"Likelihood of specified outcome as a qualitative value","type":"CodeableConcept","binding":{"strength":"example","valueSet":"http://hl7.org/fhir/ValueSet/risk-probability","bindingName":"RiskAssessmentProbability"},"index":23},"relativeRisk":{"short":"Relative likelihood","type":"decimal","index":24},"when":{"short":"Timeframe or age range","choices":["whenPeriod","whenRange"],"index":26},"whenPeriod":{"short":"Timeframe or age range","type":"Period","choiceOf":"when","index":27},"whenRange":{"short":"Timeframe or age range","type":"Range","choiceOf":"when","index":28},"rationale":{"short":"Explanation of prediction","type":"string","index":29}}},"mitigation":{"short":"How to reduce risk","type":"string","index":30},"note":{"short":"Comments on the risk assessment","type":"Annotation","array":true,"index":31}},"required":["status","subject"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} {"name":"RiskEvidenceSynthesis","type":"RiskEvidenceSynthesis","kind":"resource","class":"resource","url":"http://hl7.org/fhir/StructureDefinition/RiskEvidenceSynthesis","version":"4.0.1","description":"The RiskEvidenceSynthesis resource describes the likelihood of an outcome in a population plus exposure state where the risk estimate is derived from a combination of research studies.","derivation":"specialization","base":"http://hl7.org/fhir/StructureDefinition/DomainResource","elements":{"url":{"short":"Canonical identifier for this risk evidence synthesis, represented as a URI (globally unique)","type":"uri","isSummary":true,"index":0},"identifier":{"short":"Additional identifier for the risk evidence synthesis","type":"Identifier","isSummary":true,"array":true,"index":1},"version":{"short":"Business version of the risk evidence synthesis","type":"string","isSummary":true,"index":2},"name":{"short":"Name for this risk evidence synthesis (computer friendly)","type":"string","isSummary":true,"index":3},"title":{"short":"Name for this risk evidence synthesis (human friendly)","type":"string","isSummary":true,"index":4},"status":{"short":"draft | active | retired | unknown","type":"code","isModifier":true,"isModifierReason":"This is labeled as \\"Is Modifier\\" because applications should not use a retired {{title}} without due consideration","isSummary":true,"binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/publication-status|4.0.1","bindingName":"PublicationStatus"},"index":5},"date":{"short":"Date last changed","type":"dateTime","isSummary":true,"index":6},"publisher":{"short":"Name of the publisher (organization or individual)","type":"string","isSummary":true,"index":7},"contact":{"short":"Contact details for the publisher","type":"ContactDetail","isSummary":true,"array":true,"index":8},"description":{"short":"Natural language description of the risk evidence synthesis","type":"markdown","isSummary":true,"index":9},"note":{"short":"Used for footnotes or explanatory notes","type":"Annotation","array":true,"index":10},"useContext":{"short":"The context that the content is intended to support","type":"UsageContext","isSummary":true,"array":true,"index":11},"jurisdiction":{"short":"Intended jurisdiction for risk evidence synthesis (if applicable)","type":"CodeableConcept","isSummary":true,"binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/jurisdiction","bindingName":"Jurisdiction"},"array":true,"index":12},"copyright":{"short":"Use and/or publishing restrictions","type":"markdown","index":13},"approvalDate":{"short":"When the risk evidence synthesis was approved by publisher","type":"date","index":14},"lastReviewDate":{"short":"When the risk evidence synthesis was last reviewed","type":"date","index":15},"effectivePeriod":{"short":"When the risk evidence synthesis is expected to be used","type":"Period","isSummary":true,"index":16},"topic":{"short":"The category of the EffectEvidenceSynthesis, such as Education, Treatment, Assessment, etc.","type":"CodeableConcept","binding":{"strength":"example","valueSet":"http://hl7.org/fhir/ValueSet/definition-topic","bindingName":"DefinitionTopic"},"array":true,"index":17},"author":{"short":"Who authored the content","type":"ContactDetail","array":true,"index":18},"editor":{"short":"Who edited the content","type":"ContactDetail","array":true,"index":19},"reviewer":{"short":"Who reviewed the content","type":"ContactDetail","array":true,"index":20},"endorser":{"short":"Who endorsed the content","type":"ContactDetail","array":true,"index":21},"relatedArtifact":{"short":"Additional documentation, citations, etc.","type":"RelatedArtifact","array":true,"index":22},"synthesisType":{"short":"Type of synthesis","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/synthesis-type","bindingName":"SynthesisType"},"index":23},"studyType":{"short":"Type of study","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/study-type","bindingName":"StudyType"},"index":24},"population":{"short":"What population?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/EvidenceVariable"],"index":25},"exposure":{"short":"What exposure?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/EvidenceVariable"],"index":26},"outcome":{"short":"What outcome?","type":"Reference","isSummary":true,"refers":["http://hl7.org/fhir/StructureDefinition/EvidenceVariable"],"index":27},"sampleSize":{"short":"What sample size was involved?","type":"BackboneElement","index":28,"elements":{"description":{"short":"Description of sample size","type":"string","index":29},"numberOfStudies":{"short":"How many studies?","type":"integer","index":30},"numberOfParticipants":{"short":"How many participants?","type":"integer","index":31}}},"riskEstimate":{"short":"What was the estimated risk","type":"BackboneElement","isSummary":true,"index":32,"elements":{"description":{"short":"Description of risk estimate","type":"string","index":33},"type":{"short":"Type of risk estimate","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/risk-estimate-type","bindingName":"RiskEstimateType"},"index":34},"value":{"short":"Point estimate","type":"decimal","index":35},"unitOfMeasure":{"short":"What unit is the outcome described in?","type":"CodeableConcept","binding":{"strength":"required","valueSet":"http://hl7.org/fhir/ValueSet/ucum-units|4.0.1","bindingName":"UCUMUnits"},"index":36},"denominatorCount":{"short":"Sample size for group measured","type":"integer","index":37},"numeratorCount":{"short":"Number with the outcome","type":"integer","index":38},"precisionEstimate":{"short":"How precise the estimate is","type":"BackboneElement","array":true,"index":39,"elements":{"type":{"short":"Type of precision estimate","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/precision-estimate-type","bindingName":"PrecisionEstimateType"},"index":40},"level":{"short":"Level of confidence interval","type":"decimal","index":41},"from":{"short":"Lower bound","type":"decimal","index":42},"to":{"short":"Upper bound","type":"decimal","index":43}}}}},"certainty":{"short":"How certain is the risk","type":"BackboneElement","array":true,"index":44,"elements":{"rating":{"short":"Certainty rating","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/evidence-quality","bindingName":"QualityOfEvidenceRating"},"array":true,"index":45},"note":{"short":"Used for footnotes or explanatory notes","type":"Annotation","array":true,"index":46},"certaintySubcomponent":{"short":"A component that contributes to the overall certainty","type":"BackboneElement","array":true,"index":47,"elements":{"type":{"short":"Type of subcomponent of certainty rating","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/certainty-subcomponent-type","bindingName":"CertaintySubcomponentType"},"index":48},"rating":{"short":"Subcomponent certainty rating","type":"CodeableConcept","binding":{"strength":"extensible","valueSet":"http://hl7.org/fhir/ValueSet/certainty-subcomponent-rating","bindingName":"CertaintySubcomponentRating"},"array":true,"index":49},"note":{"short":"Used for footnotes or explanatory notes","type":"Annotation","array":true,"index":50}}}}}},"required":["outcome","population","status"],"package_meta":{"name":"hl7.fhir.r4.core","version":"4.0.1"}} From 7b8ebb6b19fb3db6957c8feee78585fbe508cc96 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Tue, 24 Mar 2026 15:11:52 +0100 Subject: [PATCH 4/7] ref: Simplify transformFhirSchema with early returns --- src/typeschema/core/transformer.ts | 64 ++++++++++++++++-------------- src/typeschema/types.ts | 18 ++++----- 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/src/typeschema/core/transformer.ts b/src/typeschema/core/transformer.ts index 3b96786ec..6d3c0564f 100644 --- a/src/typeschema/core/transformer.ts +++ b/src/typeschema/core/transformer.ts @@ -152,47 +152,53 @@ export function transformFhirSchema(register: Register, fhirSchema: RichFHIRSche const fields = mkFields(register, fhirSchema, [], fhirSchema.elements, logger); const nested = mkNestedTypes(register, fhirSchema, logger); + const bindingSchemas = collectBindingSchemas(register, fhirSchema, logger); - let typeSchema: TypeSchema; if (fhirSchema.derivation === "constraint") { const identifier = mkIdentifier(fhirSchema); if (!base) throw new Error(`Profile ${fhirSchema.url} must have a base type`); const extensions = extractProfileExtensions(register, fhirSchema, logger); const extensionDeps = extensions?.flatMap(extractExtensionDeps); const rawDeps = extractProfileDependencies(identifier, base, fields, nested); - typeSchema = { - identifier, - base, - fields, - nested, - description: fhirSchema.description, - dependencies: concatIdentifiers(rawDeps, extensionDeps), - extensions, - }; - } else if (fhirSchema.kind === "primitive-type") { + return [ + { + identifier, + base, + fields, + nested, + description: fhirSchema.description, + dependencies: concatIdentifiers(rawDeps, extensionDeps), + extensions, + }, + ...bindingSchemas, + ]; + } + + if (fhirSchema.kind === "primitive-type") { const identifier = mkIdentifier(fhirSchema); - const rawDeps = extractDependencies(identifier, base, fields, nested); assert(base, `Primitive type ${fhirSchema.url} must have a base type`); - typeSchema = { - identifier, - description: fhirSchema.description, - base, - dependencies: rawDeps, - }; - } else { - const identifier = mkIdentifier(fhirSchema); - const rawDeps = extractDependencies(identifier, base, fields, nested); - typeSchema = { + return [ + { + identifier, + description: fhirSchema.description, + base, + dependencies: extractDependencies(identifier, base, fields, nested), + }, + ...bindingSchemas, + ]; + } + + const identifier = mkIdentifier(fhirSchema); + return [ + { identifier, base, fields, nested, description: fhirSchema.description, - dependencies: rawDeps, - typeFamily: undefined, // NOTE: should be populateTypeFamily later. - }; - } - - const bindingSchemas = collectBindingSchemas(register, fhirSchema, logger); - return [typeSchema, ...bindingSchemas]; + dependencies: extractDependencies(identifier, base, fields, nested), + typeFamily: undefined, + }, + ...bindingSchemas, + ]; } diff --git a/src/typeschema/types.ts b/src/typeschema/types.ts index c042408c2..a853de09c 100644 --- a/src/typeschema/types.ts +++ b/src/typeschema/types.ts @@ -181,9 +181,9 @@ export type TypeSchema = | BindingTypeSchema | ProfileTypeSchema; -type SchemaGuardInput = { identifier: TypeIdentifier } | undefined; +type TypeSchemaGuardInput = TypeSchema | NestedTypeSchema | undefined; -export const isSpecializationTypeSchema = (schema: SchemaGuardInput): schema is SpecializationTypeSchema => { +export const isSpecializationTypeSchema = (schema: TypeSchemaGuardInput): schema is SpecializationTypeSchema => { return ( schema?.identifier.kind === "resource" || schema?.identifier.kind === "complex-type" || @@ -191,31 +191,31 @@ export const isSpecializationTypeSchema = (schema: SchemaGuardInput): schema is ); }; -export const isComplexTypeTypeSchema = (schema: SchemaGuardInput): schema is ComplexTypeTypeSchema => { +export const isComplexTypeTypeSchema = (schema: TypeSchemaGuardInput): schema is ComplexTypeTypeSchema => { return schema?.identifier.kind === "complex-type"; }; -export const isResourceTypeSchema = (schema: SchemaGuardInput): schema is ResourceTypeSchema => { +export const isResourceTypeSchema = (schema: TypeSchemaGuardInput): schema is ResourceTypeSchema => { return schema?.identifier.kind === "resource"; }; -export const isPrimitiveTypeSchema = (schema: SchemaGuardInput): schema is PrimitiveTypeSchema => { +export const isPrimitiveTypeSchema = (schema: TypeSchemaGuardInput): schema is PrimitiveTypeSchema => { return schema?.identifier.kind === "primitive-type"; }; -export const isLogicalTypeSchema = (schema: SchemaGuardInput): schema is LogicalTypeSchema => { +export const isLogicalTypeSchema = (schema: TypeSchemaGuardInput): schema is LogicalTypeSchema => { return schema?.identifier.kind === "logical"; }; -export const isProfileTypeSchema = (schema: SchemaGuardInput): schema is ProfileTypeSchema => { +export const isProfileTypeSchema = (schema: TypeSchemaGuardInput): schema is ProfileTypeSchema => { return schema?.identifier.kind === "profile"; }; -export const isBindingSchema = (schema: SchemaGuardInput): schema is BindingTypeSchema => { +export const isBindingSchema = (schema: TypeSchemaGuardInput): schema is BindingTypeSchema => { return schema?.identifier.kind === "binding"; }; -export const isValueSetTypeSchema = (schema: SchemaGuardInput): schema is ValueSetTypeSchema => { +export const isValueSetTypeSchema = (schema: TypeSchemaGuardInput): schema is ValueSetTypeSchema => { return schema?.identifier.kind === "value-set"; }; From 4e6bfb2b882661cac205276f53444bb0458f75f2 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Tue, 24 Mar 2026 16:05:47 +0100 Subject: [PATCH 5/7] ref: Split resolve into resolve(Identifier) and resolveType(TypeIdentifier) - resolve(id: Identifier) returns TypeSchema only, no nested - resolveType(id: TypeIdentifier) returns TypeSchema | NestedTypeSchema - nestedIndex properly typed as Record<..., NestedTypeSchema> - Callers passing TypeIdentifier migrated to resolveType --- .../mustache/generator/ViewModelFactory.ts | 8 ++--- .../typescript/profile-slices.ts | 2 +- .../writer-generator/typescript/profile.ts | 2 +- src/api/writer-generator/typescript/writer.ts | 2 +- src/typeschema/ir/tree-shake.ts | 4 ++- src/typeschema/types.ts | 4 +++ src/typeschema/utils.ts | 35 ++++++++++++++----- 7 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/api/mustache/generator/ViewModelFactory.ts b/src/api/mustache/generator/ViewModelFactory.ts index 282368978..302bde76b 100644 --- a/src/api/mustache/generator/ViewModelFactory.ts +++ b/src/api/mustache/generator/ViewModelFactory.ts @@ -100,7 +100,7 @@ export class ViewModelFactory { cache: ViewModelCache, nestedIn?: TypeSchema, ): TypeViewModel { - const type = this.tsIndex.resolve(typeRef); + const type = this.tsIndex.resolveType(typeRef); if (!type) { throw new Error(`ComplexType ${typeRef.name} not found`); } @@ -113,7 +113,7 @@ export class ViewModelFactory { } private _createForResource(typeRef: TypeIdentifier, cache: ViewModelCache, nestedIn?: TypeSchema): TypeViewModel { - const type = this.tsIndex.resolve(typeRef); + const type = this.tsIndex.resolveType(typeRef); if (!type) { throw new Error(`Resource ${typeRef.name} not found`); } @@ -126,7 +126,7 @@ export class ViewModelFactory { } private _createChildrenFor(typeRef: TypeIdentifier, cache: ViewModelCache, nestedIn?: TypeSchema): TypeViewModel[] { - const schema = this.tsIndex.resolve(typeRef); + const schema = this.tsIndex.resolveType(typeRef); if (!schema) return []; if (isComplexTypeTypeSchema(schema)) { return (schema.typeFamily?.complexTypes ?? []) @@ -146,7 +146,7 @@ export class ViewModelFactory { let parentRef: TypeIdentifier | undefined = "base" in base ? base.base : undefined; while (parentRef) { parents.push(this._createFor(parentRef, cache, undefined)); - const parent = this.tsIndex.resolve(parentRef); + const parent = this.tsIndex.resolveType(parentRef); parentRef = parent && "base" in parent ? parent.base : undefined; } return parents; diff --git a/src/api/writer-generator/typescript/profile-slices.ts b/src/api/writer-generator/typescript/profile-slices.ts index 1166a08b7..73611b617 100644 --- a/src/api/writer-generator/typescript/profile-slices.ts +++ b/src/api/writer-generator/typescript/profile-slices.ts @@ -22,7 +22,7 @@ import type { TypeScript } from "./writer"; /** Collect choice declaration field names from a base type schema */ const collectChoiceBaseNames = (tsIndex: TypeSchemaIndex, typeId: TypeIdentifier): Set => { const names = new Set(); - const schema = tsIndex.resolve(typeId); + const schema = tsIndex.resolveType(typeId); if (schema && "fields" in schema && schema.fields) { for (const [name, f] of Object.entries(schema.fields)) { if (isChoiceDeclarationField(f)) names.add(name); diff --git a/src/api/writer-generator/typescript/profile.ts b/src/api/writer-generator/typescript/profile.ts index e9f420c09..78cbc3c9e 100644 --- a/src/api/writer-generator/typescript/profile.ts +++ b/src/api/writer-generator/typescript/profile.ts @@ -168,7 +168,7 @@ const collectBaseRequiredParams = ( coveredNames: string[], ) => { const covered = new Set(coveredNames); - const baseSchema = tsIndex.resolve(flatProfile.base); + const baseSchema = tsIndex.resolveType(flatProfile.base); if (!baseSchema || !("fields" in baseSchema) || !baseSchema.fields) return; for (const [name, field] of Object.entries(baseSchema.fields)) { if (covered.has(name)) continue; diff --git a/src/api/writer-generator/typescript/writer.ts b/src/api/writer-generator/typescript/writer.ts index 7208eb711..63342d8c9 100644 --- a/src/api/writer-generator/typescript/writer.ts +++ b/src/api/writer-generator/typescript/writer.ts @@ -213,7 +213,7 @@ export class TypeScript extends Writer { const typeFamilyFields: { fieldName: string; familyTypeName: string }[] = []; for (const [fieldName, field] of Object.entries(schema.fields ?? {})) { if (isChoiceDeclarationField(field) || !field.type) continue; - const fieldTypeSchema = tsIndex.resolve(field.type); + const fieldTypeSchema = tsIndex.resolveType(field.type); if ( isSpecializationTypeSchema(fieldTypeSchema) && (fieldTypeSchema.typeFamily?.resources?.length ?? 0) > 0 diff --git a/src/typeschema/ir/tree-shake.ts b/src/typeschema/ir/tree-shake.ts index 77669c9aa..7c2730094 100644 --- a/src/typeschema/ir/tree-shake.ts +++ b/src/typeschema/ir/tree-shake.ts @@ -10,6 +10,7 @@ import { isChoiceDeclarationField, isChoiceInstanceField, isNestedIdentifier, + isNestedTypeSchema, isNotChoiceDeclarationField, isPrimitiveTypeSchema, isProfileTypeSchema, @@ -242,7 +243,7 @@ export const treeShake = (tsIndex: TypeSchemaIndex, treeShake: TreeShakeConf): T for (const [pkgId, requires] of Object.entries(treeShake)) { for (const [url, rule] of Object.entries(requires)) { const schema = tsIndex.resolveByUrl(pkgId, url as CanonicalUrl); - if (!schema) throw new Error(`Schema not found for ${pkgId} ${url}`); + if (!schema || isNestedTypeSchema(schema)) throw new Error(`Schema not found for ${pkgId} ${url}`); const shaked = treeShakeTypeSchema(schema, rule); focusedSchemas.push(shaked); } @@ -259,6 +260,7 @@ export const treeShake = (tsIndex: TypeSchemaIndex, treeShake: TreeShakeConf): T if (isSpecializationTypeSchema(schema) || isProfileTypeSchema(schema)) { if (!schema.dependencies) continue; schema.dependencies.forEach((dep) => { + if (isNestedIdentifier(dep)) return; const depSchema = tsIndex.resolve(dep); if (!depSchema) throw new Error( diff --git a/src/typeschema/types.ts b/src/typeschema/types.ts index a853de09c..40d1e430f 100644 --- a/src/typeschema/types.ts +++ b/src/typeschema/types.ts @@ -183,6 +183,10 @@ export type TypeSchema = type TypeSchemaGuardInput = TypeSchema | NestedTypeSchema | undefined; +export const isNestedTypeSchema = (schema: TypeSchemaGuardInput): schema is NestedTypeSchema => { + return schema !== undefined && isNestedIdentifier(schema.identifier); +}; + export const isSpecializationTypeSchema = (schema: TypeSchemaGuardInput): schema is SpecializationTypeSchema => { return ( schema?.identifier.kind === "resource" || diff --git a/src/typeschema/utils.ts b/src/typeschema/utils.ts index 651462683..87c46dda9 100644 --- a/src/typeschema/utils.ts +++ b/src/typeschema/utils.ts @@ -10,11 +10,14 @@ import { type ComplexTypeTypeSchema, type ConstrainedChoiceInfo, type Field, + type Identifier, isChoiceDeclarationField, isChoiceInstanceField, isComplexTypeIdentifier, isComplexTypeTypeSchema, isLogicalTypeSchema, + isNestedIdentifier, + isNestedTypeSchema, isProfileTypeSchema, isResourceIdentifier, isResourceTypeSchema, @@ -159,8 +162,9 @@ export type TypeSchemaIndex = { collectResources: () => ResourceTypeSchema[]; collectLogicalModels: () => LogicalTypeSchema[]; collectProfiles: () => ProfileTypeSchema[]; - resolve: (id: TypeIdentifier) => TypeSchema | undefined; - resolveByUrl: (pkgName: PkgName, url: CanonicalUrl) => TypeSchema | undefined; + resolve: (id: Identifier) => TypeSchema | undefined; + resolveType: (id: TypeIdentifier) => TypeSchema | NestedTypeSchema | undefined; + resolveByUrl: (pkgName: PkgName, url: CanonicalUrl) => TypeSchema | NestedTypeSchema | undefined; tryHierarchy: (schema: TypeSchema) => TypeSchema[] | undefined; hierarchy: (schema: TypeSchema) => TypeSchema[]; findLastSpecialization: (schema: TypeSchema) => TypeSchema; @@ -223,11 +227,14 @@ export const mkTypeSchemaIndex = ( } populateTypeFamily(schemas); - const resolve = (id: TypeIdentifier): TypeSchema | undefined => { - if (id.kind === "nested") return nestedIndex[id.url]?.[id.package] as unknown as TypeSchema | undefined; + const resolve = (id: Identifier): TypeSchema | undefined => { return index[id.url]?.[id.package]; }; - const resolveByUrl = (pkgName: PkgName, url: CanonicalUrl) => { + const resolveType = (id: TypeIdentifier): TypeSchema | NestedTypeSchema | undefined => { + if (isNestedIdentifier(id)) return nestedIndex[id.url]?.[id.package]; + return index[id.url]?.[id.package]; + }; + const resolveByUrl = (pkgName: PkgName, url: CanonicalUrl): TypeSchema | NestedTypeSchema | undefined => { if (register) { const resolutionTree = register.resolutionTree(); const resolution = resolutionTree[pkgName]?.[url]?.[0]; @@ -236,7 +243,7 @@ export const mkTypeSchemaIndex = ( } } if (index[url]?.[pkgName]) return index[url]?.[pkgName]; - if (nestedIndex[url]?.[pkgName]) return nestedIndex[url]?.[pkgName] as unknown as TypeSchema; + if (nestedIndex[url]?.[pkgName]) return nestedIndex[url]?.[pkgName]; logger?.dryWarn(`Type '${url}' not found in '${pkgName}'`); // Fallback: search across all packages when type exists elsewhere @@ -247,6 +254,13 @@ export const mkTypeSchemaIndex = ( return index[url]?.[anyPkg]; } } + if (nestedIndex[url]) { + const anyPkg = Object.keys(nestedIndex[url])[0]; + if (anyPkg) { + logger?.dryWarn(`Type '${url}' fallback to package ${anyPkg}`); + return nestedIndex[url]?.[anyPkg]; + } + } return undefined; }; @@ -257,6 +271,7 @@ export const mkTypeSchemaIndex = ( res.push(cur); const base = (cur as SpecializationTypeSchema).base; if (base === undefined) break; + if (isNestedIdentifier(base)) break; const resolved = resolve(base); if (!resolved) { logger?.warn( @@ -287,9 +302,10 @@ export const mkTypeSchemaIndex = ( }; const findLastSpecializationByIdentifier = (id: TypeIdentifier): TypeIdentifier => { - const schema = resolve(id); - if (!schema) return id; - return findLastSpecialization(schema).identifier; + const resolved = resolveType(id); + if (!resolved) return id; + if (isNestedTypeSchema(resolved)) return findLastSpecializationByIdentifier(resolved.base); + return findLastSpecialization(resolved).identifier; }; /** Narrow choice declarations by finding the most derived schema that constrains each choice group. @@ -461,6 +477,7 @@ export const mkTypeSchemaIndex = ( collectLogicalModels: () => schemas.filter(isLogicalTypeSchema), collectProfiles: () => schemas.filter(isProfileTypeSchema), resolve, + resolveType, resolveByUrl, tryHierarchy, hierarchy, From ddb1e6fe988131dcbb19585e2e665f0dde88014d Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Tue, 24 Mar 2026 16:22:08 +0100 Subject: [PATCH 6/7] chore: Update example type-tree after resolve split --- examples/typescript-r4/fhir-types/type-tree.yaml | 4 +--- examples/typescript-us-core/fhir-types/type-tree.yaml | 5 +---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/examples/typescript-r4/fhir-types/type-tree.yaml b/examples/typescript-r4/fhir-types/type-tree.yaml index 729bf485e..c7440755f 100644 --- a/examples/typescript-r4/fhir-types/type-tree.yaml +++ b/examples/typescript-r4/fhir-types/type-tree.yaml @@ -64,9 +64,7 @@ hl7.fhir.r4.core: http://hl7.org/fhir/StructureDefinition/Patient: {} http://hl7.org/fhir/StructureDefinition/Resource: {} value-set: {} - nested: - http://hl7.org/fhir/StructureDefinition/Observation#component: {} - http://hl7.org/fhir/StructureDefinition/Observation#referenceRange: {} + nested: {} binding: {} profile: http://hl7.org/fhir/StructureDefinition/patient-birthPlace: {} diff --git a/examples/typescript-us-core/fhir-types/type-tree.yaml b/examples/typescript-us-core/fhir-types/type-tree.yaml index e720815bd..7142c3d98 100644 --- a/examples/typescript-us-core/fhir-types/type-tree.yaml +++ b/examples/typescript-us-core/fhir-types/type-tree.yaml @@ -85,10 +85,7 @@ hl7.fhir.r4.core: http://hl7.org/fhir/StructureDefinition/Patient: {} http://hl7.org/fhir/StructureDefinition/Resource: {} value-set: {} - nested: - http://hl7.org/fhir/StructureDefinition/Patient#communication: {} - http://hl7.org/fhir/StructureDefinition/Observation#component: {} - http://hl7.org/fhir/StructureDefinition/Observation#referenceRange: {} + nested: {} binding: {} profile: http://hl7.org/fhir/StructureDefinition/vitalsigns: {} From 1045526f2e9314d0f221b373479567326746bf9f Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Tue, 24 Mar 2026 16:41:43 +0100 Subject: [PATCH 7/7] ref: Make SpecializationTypeSchema a proper discriminated union --- src/typeschema/core/transformer.ts | 23 +++++++++++------------ src/typeschema/types.ts | 10 ++++------ 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/typeschema/core/transformer.ts b/src/typeschema/core/transformer.ts index 6d3c0564f..5ae8f0383 100644 --- a/src/typeschema/core/transformer.ts +++ b/src/typeschema/core/transformer.ts @@ -20,6 +20,7 @@ import { packageMetaToFhir, type RichFHIRSchema, type RichValueSet, + type SpecializationTypeSchema, type TypeIdentifier, type TypeSchema, type ValueSetTypeSchema, @@ -189,16 +190,14 @@ export function transformFhirSchema(register: Register, fhirSchema: RichFHIRSche } const identifier = mkIdentifier(fhirSchema); - return [ - { - identifier, - base, - fields, - nested, - description: fhirSchema.description, - dependencies: extractDependencies(identifier, base, fields, nested), - typeFamily: undefined, - }, - ...bindingSchemas, - ]; + const schema = { + identifier, + base, + fields, + nested, + description: fhirSchema.description, + dependencies: extractDependencies(identifier, base, fields, nested), + typeFamily: undefined, + } as SpecializationTypeSchema; + return [schema, ...bindingSchemas]; } diff --git a/src/typeschema/types.ts b/src/typeschema/types.ts index 40d1e430f..6dc751591 100644 --- a/src/typeschema/types.ts +++ b/src/typeschema/types.ts @@ -313,13 +313,11 @@ export type TypeFamily = { complexTypes?: ComplexTypeIdentifier[]; }; -export type SpecializationTypeSchema = { - identifier: ResourceIdentifier | ComplexTypeIdentifier | LogicalIdentifier; -} & SpecializationTypeSchemaBody; +export type ResourceTypeSchema = { identifier: ResourceIdentifier } & SpecializationTypeSchemaBody; +export type ComplexTypeTypeSchema = { identifier: ComplexTypeIdentifier } & SpecializationTypeSchemaBody; +export type LogicalTypeSchema = { identifier: LogicalIdentifier } & SpecializationTypeSchemaBody; -export type ResourceTypeSchema = SpecializationTypeSchema & { identifier: ResourceIdentifier }; -export type ComplexTypeTypeSchema = SpecializationTypeSchema & { identifier: ComplexTypeIdentifier }; -export type LogicalTypeSchema = SpecializationTypeSchema & { identifier: LogicalIdentifier }; +export type SpecializationTypeSchema = ResourceTypeSchema | ComplexTypeTypeSchema | LogicalTypeSchema; export interface RegularField { type: TypeIdentifier;