@atomic-ehr/codegen 0.0.1-canary.20251001075906.44a8bb9 → 0.0.1-canary.20251001154933.613f6c7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/index.d.ts +1 -1
- package/dist/cli/index.js +34 -34
- package/dist/typeschema/core/binding.d.ts +7 -7
- package/dist/typeschema/core/binding.js +10 -14
- package/dist/typeschema/core/field-builder.d.ts +5 -11
- package/dist/typeschema/core/field-builder.js +25 -32
- package/dist/typeschema/core/identifier.d.ts +3 -3
- package/dist/typeschema/core/nested-types.d.ts +4 -13
- package/dist/typeschema/core/nested-types.js +13 -31
- package/dist/typeschema/core/transformer.d.ts +2 -2
- package/dist/typeschema/core/transformer.js +35 -15
- package/dist/typeschema/profile/processor.js +6 -4
- package/dist/typeschema/register.d.ts +2 -0
- package/dist/typeschema/register.js +16 -4
- package/dist/typeschema/types.d.ts +7 -14
- package/package.json +1 -1
|
@@ -3,23 +3,23 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Functions for processing value set bindings and generating enums
|
|
5
5
|
*/
|
|
6
|
-
import type { CanonicalManager } from "@atomic-ehr/fhir-canonical-manager";
|
|
7
6
|
import type { FHIRSchemaElement } from "@atomic-ehr/fhirschema";
|
|
8
|
-
import type {
|
|
7
|
+
import type { Register } from "@typeschema/register";
|
|
8
|
+
import type { CanonicalUrl, PackageMeta, RichFHIRSchema, TypeSchemaForBinding } from "@typeschema/types";
|
|
9
9
|
/**
|
|
10
10
|
* Extract concepts from a ValueSet
|
|
11
11
|
*/
|
|
12
|
-
export declare function extractValueSetConcepts(valueSetUrl:
|
|
12
|
+
export declare function extractValueSetConcepts(valueSetUrl: CanonicalUrl, register: Register): {
|
|
13
13
|
system: string;
|
|
14
14
|
code: string;
|
|
15
15
|
display?: string;
|
|
16
|
-
}
|
|
16
|
+
}[] | undefined;
|
|
17
17
|
/**
|
|
18
18
|
* Build enum values from binding if applicable
|
|
19
19
|
*/
|
|
20
|
-
export declare function buildEnum(element: FHIRSchemaElement,
|
|
21
|
-
export declare function generateBindingSchema(fhirSchema: RichFHIRSchema, path: string[], element: FHIRSchemaElement,
|
|
20
|
+
export declare function buildEnum(element: FHIRSchemaElement, register: Register): string[] | undefined;
|
|
21
|
+
export declare function generateBindingSchema(fhirSchema: RichFHIRSchema, path: string[], element: FHIRSchemaElement, register: Register, packageInfo?: PackageMeta): Promise<TypeSchemaForBinding | undefined>;
|
|
22
22
|
/**
|
|
23
23
|
* Collect all binding schemas from a FHIRSchema
|
|
24
24
|
*/
|
|
25
|
-
export declare function collectBindingSchemas(fhirSchema: RichFHIRSchema,
|
|
25
|
+
export declare function collectBindingSchemas(fhirSchema: RichFHIRSchema, register: Register): Promise<TypeSchemaForBinding[]>;
|
|
@@ -8,10 +8,10 @@ import { dropVersionFromUrl, mkBindingIdentifier, mkValueSetIdentifier } from ".
|
|
|
8
8
|
/**
|
|
9
9
|
* Extract concepts from a ValueSet
|
|
10
10
|
*/
|
|
11
|
-
export
|
|
11
|
+
export function extractValueSetConcepts(valueSetUrl, register) {
|
|
12
12
|
try {
|
|
13
13
|
const cleanUrl = dropVersionFromUrl(valueSetUrl) || valueSetUrl;
|
|
14
|
-
const valueSet =
|
|
14
|
+
const valueSet = register.resolveVs(cleanUrl);
|
|
15
15
|
if (!valueSet)
|
|
16
16
|
return undefined;
|
|
17
17
|
// If expansion is available, use it
|
|
@@ -39,7 +39,7 @@ export async function extractValueSetConcepts(valueSetUrl, manager) {
|
|
|
39
39
|
else if (include.system && !include.filter) {
|
|
40
40
|
// Include all from CodeSystem
|
|
41
41
|
try {
|
|
42
|
-
const codeSystem =
|
|
42
|
+
const codeSystem = register.resolveAny(include.system);
|
|
43
43
|
if (codeSystem?.concept) {
|
|
44
44
|
const extractConcepts = (conceptList, system) => {
|
|
45
45
|
for (const concept of conceptList) {
|
|
@@ -72,7 +72,7 @@ export async function extractValueSetConcepts(valueSetUrl, manager) {
|
|
|
72
72
|
/**
|
|
73
73
|
* Build enum values from binding if applicable
|
|
74
74
|
*/
|
|
75
|
-
export
|
|
75
|
+
export function buildEnum(element, register) {
|
|
76
76
|
if (!element.binding)
|
|
77
77
|
return undefined;
|
|
78
78
|
const { strength, valueSet } = element.binding;
|
|
@@ -90,12 +90,8 @@ export async function buildEnum(element, manager) {
|
|
|
90
90
|
if (!shouldGenerateEnum) {
|
|
91
91
|
return undefined;
|
|
92
92
|
}
|
|
93
|
-
// Check if manager is available and functional
|
|
94
|
-
if (!manager.resolve) {
|
|
95
|
-
return undefined;
|
|
96
|
-
}
|
|
97
93
|
try {
|
|
98
|
-
const concepts =
|
|
94
|
+
const concepts = extractValueSetConcepts(valueSet, register);
|
|
99
95
|
if (!concepts || concepts.length === 0)
|
|
100
96
|
return undefined;
|
|
101
97
|
// Extract just the codes and filter out any empty/invalid ones
|
|
@@ -112,18 +108,18 @@ export async function buildEnum(element, manager) {
|
|
|
112
108
|
return undefined;
|
|
113
109
|
}
|
|
114
110
|
}
|
|
115
|
-
export async function generateBindingSchema(fhirSchema, path, element,
|
|
111
|
+
export async function generateBindingSchema(fhirSchema, path, element, register, packageInfo) {
|
|
116
112
|
if (!element.binding?.valueSet)
|
|
117
113
|
return undefined;
|
|
118
114
|
const identifier = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName, packageInfo);
|
|
119
|
-
const fieldType = buildFieldType(fhirSchema, path, element,
|
|
115
|
+
const fieldType = buildFieldType(fhirSchema, path, element, register, packageInfo);
|
|
120
116
|
const valueSetIdentifier = mkValueSetIdentifier(element.binding.valueSet, undefined, packageInfo);
|
|
121
117
|
const dependencies = [];
|
|
122
118
|
if (fieldType) {
|
|
123
119
|
dependencies.push(fieldType);
|
|
124
120
|
}
|
|
125
121
|
dependencies.push(valueSetIdentifier);
|
|
126
|
-
const enumValues = await buildEnum(element,
|
|
122
|
+
const enumValues = await buildEnum(element, register);
|
|
127
123
|
return {
|
|
128
124
|
identifier,
|
|
129
125
|
type: fieldType,
|
|
@@ -136,7 +132,7 @@ export async function generateBindingSchema(fhirSchema, path, element, manager,
|
|
|
136
132
|
/**
|
|
137
133
|
* Collect all binding schemas from a FHIRSchema
|
|
138
134
|
*/
|
|
139
|
-
export async function collectBindingSchemas(fhirSchema,
|
|
135
|
+
export async function collectBindingSchemas(fhirSchema, register) {
|
|
140
136
|
const packageInfo = fhirSchema.package_meta;
|
|
141
137
|
const bindings = [];
|
|
142
138
|
const processedPaths = new Set();
|
|
@@ -151,7 +147,7 @@ export async function collectBindingSchemas(fhirSchema, manager) {
|
|
|
151
147
|
processedPaths.add(pathKey);
|
|
152
148
|
// Generate binding if present
|
|
153
149
|
if (element.binding) {
|
|
154
|
-
const binding = await generateBindingSchema(fhirSchema, path, element,
|
|
150
|
+
const binding = await generateBindingSchema(fhirSchema, path, element, register, packageInfo);
|
|
155
151
|
if (binding) {
|
|
156
152
|
bindings.push(binding);
|
|
157
153
|
}
|
|
@@ -6,20 +6,14 @@
|
|
|
6
6
|
import type { CanonicalManager } from "@atomic-ehr/fhir-canonical-manager";
|
|
7
7
|
import type { FHIRSchema, FHIRSchemaElement } from "@atomic-ehr/fhirschema";
|
|
8
8
|
import type { Register } from "@root/typeschema/register";
|
|
9
|
-
import type { Identifier, PackageMeta,
|
|
10
|
-
export declare function isRequired(fhirSchema: FHIRSchema, path: string[]
|
|
11
|
-
|
|
12
|
-
* Check if a field is excluded
|
|
13
|
-
*/
|
|
14
|
-
export declare function isExcluded(fhirSchema: FHIRSchema, path: string[], _manager: ReturnType<typeof CanonicalManager>): boolean;
|
|
9
|
+
import type { Field, Identifier, PackageMeta, RegularField, RichFHIRSchema } from "../types";
|
|
10
|
+
export declare function isRequired(fhirSchema: FHIRSchema, path: string[]): boolean;
|
|
11
|
+
export declare function isExcluded(fhirSchema: FHIRSchema, path: string[]): boolean;
|
|
15
12
|
export declare const buildReferences: (element: FHIRSchemaElement, register: Register, _packageInfo?: PackageMeta) => Identifier[] | undefined;
|
|
16
13
|
/**
|
|
17
14
|
* Build field type identifier
|
|
18
15
|
*/
|
|
19
16
|
export declare function buildFieldType(fhirSchema: RichFHIRSchema, _path: string[], element: FHIRSchemaElement, _manager: ReturnType<typeof CanonicalManager>, packageInfo?: PackageMeta): Identifier | undefined;
|
|
20
|
-
export declare const
|
|
21
|
-
/**
|
|
22
|
-
* Check if an element represents a nested type (BackboneElement)
|
|
23
|
-
*/
|
|
17
|
+
export declare const mkField: (register: Register, fhirSchema: RichFHIRSchema, path: string[], element: FHIRSchemaElement) => Field;
|
|
24
18
|
export declare function isNestedElement(element: FHIRSchemaElement): boolean;
|
|
25
|
-
export declare function mkNestedField(fhirSchema: RichFHIRSchema, path: string[], element: FHIRSchemaElement
|
|
19
|
+
export declare function mkNestedField(_register: Register, fhirSchema: RichFHIRSchema, path: string[], element: FHIRSchemaElement): RegularField;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { buildEnum } from "./binding";
|
|
7
7
|
import { mkBindingIdentifier, mkIdentifier, mkNestedIdentifier } from "./identifier";
|
|
8
|
-
export function isRequired(fhirSchema, path
|
|
8
|
+
export function isRequired(fhirSchema, path) {
|
|
9
9
|
if (path.length === 0)
|
|
10
10
|
return false;
|
|
11
11
|
const fieldName = path[path.length - 1];
|
|
@@ -27,10 +27,7 @@ export function isRequired(fhirSchema, path, _manager) {
|
|
|
27
27
|
}
|
|
28
28
|
return false;
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
* Check if a field is excluded
|
|
32
|
-
*/
|
|
33
|
-
export function isExcluded(fhirSchema, path, _manager) {
|
|
30
|
+
export function isExcluded(fhirSchema, path) {
|
|
34
31
|
if (path.length === 0)
|
|
35
32
|
return false;
|
|
36
33
|
const fieldName = path[path.length - 1];
|
|
@@ -94,51 +91,47 @@ export function buildFieldType(fhirSchema, _path, element, _manager, packageInfo
|
|
|
94
91
|
}
|
|
95
92
|
return undefined;
|
|
96
93
|
}
|
|
97
|
-
export const
|
|
94
|
+
export const mkField = (register, fhirSchema, path, element) => {
|
|
98
95
|
let binding;
|
|
99
|
-
let
|
|
96
|
+
let enumValues;
|
|
100
97
|
if (element.binding) {
|
|
101
|
-
binding = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName,
|
|
98
|
+
binding = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName, fhirSchema.package_meta);
|
|
102
99
|
if (element.binding.strength === "required" && element.type === "code") {
|
|
103
|
-
|
|
100
|
+
enumValues = buildEnum(element, register);
|
|
104
101
|
}
|
|
105
102
|
}
|
|
106
103
|
return {
|
|
107
|
-
type: buildFieldType(fhirSchema, path, element, register,
|
|
108
|
-
required: isRequired(fhirSchema, path
|
|
109
|
-
excluded: isExcluded(fhirSchema, path
|
|
110
|
-
reference: buildReferences(element, register,
|
|
104
|
+
type: buildFieldType(fhirSchema, path, element, register, fhirSchema.package_meta),
|
|
105
|
+
required: isRequired(fhirSchema, path),
|
|
106
|
+
excluded: isExcluded(fhirSchema, path),
|
|
107
|
+
reference: buildReferences(element, register, fhirSchema.package_meta),
|
|
111
108
|
array: element.array || false,
|
|
112
109
|
min: element.min,
|
|
113
110
|
max: element.max,
|
|
114
111
|
choices: element.choices,
|
|
115
112
|
choiceOf: element.choiceOf,
|
|
116
113
|
binding: binding,
|
|
114
|
+
enum: enumValues,
|
|
117
115
|
};
|
|
118
116
|
};
|
|
119
|
-
function _removeEmptyValues(obj) {
|
|
120
|
-
const result = {};
|
|
121
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
122
|
-
if (value !== undefined && value !== null) {
|
|
123
|
-
if (Array.isArray(value) && value.length === 0) {
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
result[key] = value;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return result;
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Check if an element represents a nested type (BackboneElement)
|
|
133
|
-
*/
|
|
134
117
|
export function isNestedElement(element) {
|
|
135
|
-
|
|
118
|
+
const isBackbone = element.type === "BackboneElement";
|
|
119
|
+
const isElement = element.type === "Element" && element.elements !== undefined && Object.keys(element.elements).length > 0;
|
|
120
|
+
// TODO: Observation <- vitalsigns <- bodyweight
|
|
121
|
+
// In Observation we have value[x] with choices
|
|
122
|
+
// In bodyweight we have valueQuantity with additional constaraints on it's elements
|
|
123
|
+
// So we need to build nested type from Quantity for here, but don't do that right now.
|
|
124
|
+
const elementsWithoutType = element.type === undefined &&
|
|
125
|
+
element.choiceOf === undefined &&
|
|
126
|
+
element.elements !== undefined &&
|
|
127
|
+
Object.keys(element.elements).length > 0;
|
|
128
|
+
return isBackbone || isElement || elementsWithoutType;
|
|
136
129
|
}
|
|
137
|
-
export function mkNestedField(fhirSchema, path, element
|
|
130
|
+
export function mkNestedField(_register, fhirSchema, path, element) {
|
|
138
131
|
return {
|
|
139
132
|
type: mkNestedIdentifier(fhirSchema, path),
|
|
140
133
|
array: element.array || false,
|
|
141
|
-
required: isRequired(fhirSchema, path
|
|
142
|
-
excluded: isExcluded(fhirSchema, path
|
|
134
|
+
required: isRequired(fhirSchema, path),
|
|
135
|
+
excluded: isExcluded(fhirSchema, path),
|
|
143
136
|
};
|
|
144
137
|
}
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Functions for creating TypeSchema identifiers from FHIRSchema entities
|
|
5
5
|
*/
|
|
6
6
|
import type { FHIRSchema } from "@atomic-ehr/fhirschema";
|
|
7
|
-
import type { BindingIdentifier, Identifier, NestedIdentifier, PackageMeta, RichFHIRSchema, TypeSchemaForValueSet } from "@typeschema/types";
|
|
8
|
-
export declare function dropVersionFromUrl(url:
|
|
7
|
+
import type { BindingIdentifier, CanonicalUrl, Identifier, NestedIdentifier, PackageMeta, RichFHIRSchema, TypeSchemaForValueSet } from "@typeschema/types";
|
|
8
|
+
export declare function dropVersionFromUrl(url: CanonicalUrl | undefined): CanonicalUrl | undefined;
|
|
9
9
|
export declare function mkIdentifier(fhirSchema: RichFHIRSchema): Identifier;
|
|
10
10
|
export declare function mkNestedIdentifier(fhirSchema: RichFHIRSchema, path: string[]): NestedIdentifier;
|
|
11
|
-
export declare function mkValueSetIdentifier(valueSetUrl:
|
|
11
|
+
export declare function mkValueSetIdentifier(valueSetUrl: CanonicalUrl, valueSet: any, packageInfo?: PackageMeta): TypeSchemaForValueSet["identifier"];
|
|
12
12
|
export declare function mkBindingIdentifier(fhirSchema: FHIRSchema, path: string[], bindingName?: string, _packageInfo?: PackageMeta): BindingIdentifier;
|
|
@@ -5,17 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { FHIRSchema, FHIRSchemaElement } from "@atomic-ehr/fhirschema";
|
|
7
7
|
import type { Register } from "@root/typeschema/register";
|
|
8
|
-
import type {
|
|
9
|
-
/**
|
|
10
|
-
* Collect all nested elements from a FHIRSchema
|
|
11
|
-
*/
|
|
8
|
+
import type { Field, Identifier, NestedType, PackageMeta, RichFHIRSchema } from "../types";
|
|
12
9
|
export declare function collectNestedElements(fhirSchema: FHIRSchema, parentPath: string[], elements: Record<string, FHIRSchemaElement>): Array<[string[], FHIRSchemaElement]>;
|
|
13
|
-
export declare function transformNestedElements(fhirSchema: RichFHIRSchema, parentPath: string[], elements: Record<string, FHIRSchemaElement>, register: Register,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*/
|
|
17
|
-
export declare function buildNestedTypes(fhirSchema: RichFHIRSchema, register: Register, packageInfo?: PackageMeta): Promise<any[] | undefined>;
|
|
18
|
-
/**
|
|
19
|
-
* Extract dependencies from nested types
|
|
20
|
-
*/
|
|
21
|
-
export declare function extractNestedDependencies(nestedTypes: TypeSchemaNestedType[]): Identifier[];
|
|
10
|
+
export declare function transformNestedElements(fhirSchema: RichFHIRSchema, parentPath: string[], elements: Record<string, FHIRSchemaElement>, register: Register, _packageInfo?: PackageMeta): Promise<Record<string, Field>>;
|
|
11
|
+
export declare function mkNestedTypes(register: Register, fhirSchema: RichFHIRSchema): Promise<NestedType[] | undefined>;
|
|
12
|
+
export declare function extractNestedDependencies(nestedTypes: NestedType[]): Identifier[];
|
|
@@ -3,51 +3,40 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Functions for extracting and transforming nested types from FHIRSchema
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { isNestedElement, mkField, mkNestedField } from "./field-builder";
|
|
7
7
|
import { mkNestedIdentifier } from "./identifier";
|
|
8
|
-
/**
|
|
9
|
-
* Collect all nested elements from a FHIRSchema
|
|
10
|
-
*/
|
|
11
8
|
export function collectNestedElements(fhirSchema, parentPath, elements) {
|
|
12
9
|
const nested = [];
|
|
13
10
|
for (const [key, element] of Object.entries(elements)) {
|
|
14
11
|
const path = [...parentPath, key];
|
|
15
|
-
// Add this element if it's nested (BackboneElement or has elements)
|
|
16
12
|
if (isNestedElement(element)) {
|
|
17
13
|
nested.push([path, element]);
|
|
18
14
|
}
|
|
19
|
-
// Recursively collect from child elements
|
|
20
15
|
if (element.elements) {
|
|
21
16
|
nested.push(...collectNestedElements(fhirSchema, path, element.elements));
|
|
22
17
|
}
|
|
23
18
|
}
|
|
24
19
|
return nested;
|
|
25
20
|
}
|
|
26
|
-
export async function transformNestedElements(fhirSchema, parentPath, elements, register,
|
|
21
|
+
export async function transformNestedElements(fhirSchema, parentPath, elements, register, _packageInfo) {
|
|
27
22
|
const fields = {};
|
|
28
23
|
for (const [key, element] of Object.entries(elements)) {
|
|
29
24
|
const path = [...parentPath, key];
|
|
30
25
|
if (isNestedElement(element)) {
|
|
31
|
-
|
|
32
|
-
fields[key] = mkNestedField(fhirSchema, path, element, register, packageInfo);
|
|
26
|
+
fields[key] = mkNestedField(register, fhirSchema, path, element);
|
|
33
27
|
}
|
|
34
28
|
else {
|
|
35
|
-
|
|
36
|
-
fields[key] = await buildField(fhirSchema, path, element, register, packageInfo);
|
|
29
|
+
fields[key] = mkField(register, fhirSchema, path, element);
|
|
37
30
|
}
|
|
38
31
|
}
|
|
39
32
|
return fields;
|
|
40
33
|
}
|
|
41
|
-
|
|
42
|
-
* Build TypeSchema for all nested types in a FHIRSchema
|
|
43
|
-
*/
|
|
44
|
-
export async function buildNestedTypes(fhirSchema, register, packageInfo) {
|
|
34
|
+
export async function mkNestedTypes(register, fhirSchema) {
|
|
45
35
|
if (!fhirSchema.elements)
|
|
46
36
|
return undefined;
|
|
47
|
-
const nestedTypes = [];
|
|
48
37
|
const nestedElements = collectNestedElements(fhirSchema, [], fhirSchema.elements);
|
|
49
|
-
// Filter to only include elements that have sub-elements (actual nested types)
|
|
50
38
|
const actualNested = nestedElements.filter(([_, element]) => element.elements && Object.keys(element.elements).length > 0);
|
|
39
|
+
const nestedTypes = [];
|
|
51
40
|
for (const [path, element] of actualNested) {
|
|
52
41
|
const identifier = mkNestedIdentifier(fhirSchema, path);
|
|
53
42
|
// Base is usually BackboneElement - ensure all nested types have a base
|
|
@@ -57,8 +46,8 @@ export async function buildNestedTypes(fhirSchema, register, packageInfo) {
|
|
|
57
46
|
// For BackboneElement or undefined type, always use BackboneElement as base
|
|
58
47
|
base = {
|
|
59
48
|
kind: "complex-type",
|
|
60
|
-
package:
|
|
61
|
-
version:
|
|
49
|
+
package: fhirSchema.package_meta.name,
|
|
50
|
+
version: fhirSchema.package_meta.version,
|
|
62
51
|
name: "BackboneElement",
|
|
63
52
|
url: "http://hl7.org/fhir/StructureDefinition/BackboneElement",
|
|
64
53
|
};
|
|
@@ -67,38 +56,31 @@ export async function buildNestedTypes(fhirSchema, register, packageInfo) {
|
|
|
67
56
|
// Use the specified type as base
|
|
68
57
|
base = {
|
|
69
58
|
kind: "complex-type",
|
|
70
|
-
package:
|
|
71
|
-
version:
|
|
59
|
+
package: fhirSchema.package_meta.name,
|
|
60
|
+
version: fhirSchema.package_meta.version,
|
|
72
61
|
name: element.type,
|
|
73
62
|
url: `http://hl7.org/fhir/StructureDefinition/${element.type}`,
|
|
74
63
|
};
|
|
75
64
|
}
|
|
76
65
|
// Transform sub-elements into fields
|
|
77
|
-
const fields = await transformNestedElements(fhirSchema, path, element.elements, register,
|
|
66
|
+
const fields = await transformNestedElements(fhirSchema, path, element.elements, register, fhirSchema.package_meta);
|
|
78
67
|
const nestedType = {
|
|
79
68
|
identifier,
|
|
80
|
-
base,
|
|
69
|
+
base,
|
|
81
70
|
fields,
|
|
82
71
|
};
|
|
83
72
|
nestedTypes.push(nestedType);
|
|
84
73
|
}
|
|
85
74
|
// Sort by URL for consistent output
|
|
86
75
|
nestedTypes.sort((a, b) => a.identifier.url.localeCompare(b.identifier.url));
|
|
87
|
-
return nestedTypes;
|
|
76
|
+
return nestedTypes.length === 0 ? undefined : nestedTypes;
|
|
88
77
|
}
|
|
89
|
-
/**
|
|
90
|
-
* Extract dependencies from nested types
|
|
91
|
-
*/
|
|
92
78
|
export function extractNestedDependencies(nestedTypes) {
|
|
93
79
|
const deps = [];
|
|
94
80
|
for (const nested of nestedTypes) {
|
|
95
|
-
// Add the nested type itself as a dependency
|
|
96
|
-
deps.push(nested.identifier);
|
|
97
|
-
// Add base dependency
|
|
98
81
|
if (nested.base) {
|
|
99
82
|
deps.push(nested.base);
|
|
100
83
|
}
|
|
101
|
-
// Add field type dependencies
|
|
102
84
|
for (const field of Object.values(nested.fields || {})) {
|
|
103
85
|
if ("type" in field && field.type) {
|
|
104
86
|
deps.push(field.type);
|
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { FHIRSchemaElement } from "@atomic-ehr/fhirschema";
|
|
7
7
|
import { type Register } from "@typeschema/register";
|
|
8
|
-
import type { RichFHIRSchema, TypeSchema
|
|
9
|
-
export declare function
|
|
8
|
+
import type { Field, RichFHIRSchema, TypeSchema } from "@typeschema/types";
|
|
9
|
+
export declare function mkFields(register: Register, fhirSchema: RichFHIRSchema, parentPath: string[], elements: Record<string, FHIRSchemaElement> | undefined): Promise<Record<string, Field> | undefined>;
|
|
10
10
|
export declare function transformFHIRSchema(register: Register, fhirSchema: RichFHIRSchema): Promise<TypeSchema[]>;
|
|
@@ -6,23 +6,37 @@
|
|
|
6
6
|
import { fsElementSnapshot, resolveFsElementGenealogy } from "@typeschema/register";
|
|
7
7
|
import { transformProfile } from "../profile/processor";
|
|
8
8
|
import { collectBindingSchemas } from "./binding";
|
|
9
|
-
import {
|
|
9
|
+
import { isNestedElement, mkField, mkNestedField } from "./field-builder";
|
|
10
10
|
import { mkIdentifier } from "./identifier";
|
|
11
|
-
import {
|
|
12
|
-
export async function
|
|
11
|
+
import { extractNestedDependencies, mkNestedTypes } from "./nested-types";
|
|
12
|
+
export async function mkFields(register, fhirSchema, parentPath, elements) {
|
|
13
13
|
if (!elements)
|
|
14
14
|
return undefined;
|
|
15
15
|
const geneology = register.resolveFsGenealogy(fhirSchema.url);
|
|
16
|
-
const
|
|
17
|
-
for (const [key,
|
|
16
|
+
const elems = {};
|
|
17
|
+
for (const [key, elem] of Object.entries(elements)) {
|
|
18
18
|
const path = [...parentPath, key];
|
|
19
19
|
const elemGeneology = resolveFsElementGenealogy(geneology, path);
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
const elemSnapshot = fsElementSnapshot(elemGeneology);
|
|
21
|
+
elems[key] = { elem, elemSnapshot, path };
|
|
22
|
+
}
|
|
23
|
+
for (const [_key, { elem, elemSnapshot, path }] of Object.entries(elems)) {
|
|
24
|
+
for (const choiceKey of elem?.choices || []) {
|
|
25
|
+
if (!elems[choiceKey]) {
|
|
26
|
+
const path = [...parentPath, choiceKey];
|
|
27
|
+
const elemGeneology = resolveFsElementGenealogy(geneology, path);
|
|
28
|
+
const elemSnapshot = fsElementSnapshot(elemGeneology);
|
|
29
|
+
elems[choiceKey] = { elem: undefined, elemSnapshot, path };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const fields = {};
|
|
34
|
+
for (const [key, { elem, elemSnapshot, path }] of Object.entries(elems)) {
|
|
35
|
+
if (isNestedElement(elemSnapshot)) {
|
|
36
|
+
fields[key] = mkNestedField(register, fhirSchema, path, elemSnapshot);
|
|
23
37
|
}
|
|
24
38
|
else {
|
|
25
|
-
fields[key] =
|
|
39
|
+
fields[key] = mkField(register, fhirSchema, path, elemSnapshot);
|
|
26
40
|
}
|
|
27
41
|
}
|
|
28
42
|
return fields;
|
|
@@ -115,7 +129,7 @@ async function transformValueSet(fhirSchema, _register, _packageInfo) {
|
|
|
115
129
|
/**
|
|
116
130
|
* Transform an Extension FHIRSchema to TypeSchema with extension metadata
|
|
117
131
|
*/
|
|
118
|
-
async function transformExtension(fhirSchema, register,
|
|
132
|
+
async function transformExtension(fhirSchema, register, _packageInfo) {
|
|
119
133
|
try {
|
|
120
134
|
const identifier = mkIdentifier(fhirSchema);
|
|
121
135
|
// Build base identifier if present
|
|
@@ -158,14 +172,14 @@ async function transformExtension(fhirSchema, register, packageInfo) {
|
|
|
158
172
|
}
|
|
159
173
|
// Transform elements into fields if present
|
|
160
174
|
if (fhirSchema.elements) {
|
|
161
|
-
const fields = await
|
|
175
|
+
const fields = await mkFields(register, fhirSchema, [], fhirSchema.elements);
|
|
162
176
|
if (fields && Object.keys(fields).length > 0) {
|
|
163
177
|
extensionSchema.fields = fields;
|
|
164
178
|
extensionSchema.dependencies.push(...extractFieldDependencies(fields));
|
|
165
179
|
}
|
|
166
180
|
}
|
|
167
181
|
// Build nested types
|
|
168
|
-
const nestedTypes = await
|
|
182
|
+
const nestedTypes = await mkNestedTypes(register, fhirSchema);
|
|
169
183
|
if (nestedTypes && nestedTypes.length > 0) {
|
|
170
184
|
extensionSchema.nested = nestedTypes;
|
|
171
185
|
extensionSchema.dependencies.push(...extractNestedDependencies(nestedTypes));
|
|
@@ -195,7 +209,10 @@ function extractDependencies(identifier, base, fields, nestedTypes) {
|
|
|
195
209
|
continue;
|
|
196
210
|
uniqDeps[dep.url] = dep;
|
|
197
211
|
}
|
|
198
|
-
const
|
|
212
|
+
const localNestedTypeUrls = new Set(nestedTypes?.map((nt) => nt.identifier.url));
|
|
213
|
+
const result = Object.values(uniqDeps)
|
|
214
|
+
.filter((e) => !(e.kind === "nested" && localNestedTypeUrls.has(e.url)))
|
|
215
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
199
216
|
return result.length > 0 ? result : undefined;
|
|
200
217
|
}
|
|
201
218
|
async function transformResource(register, fhirSchema) {
|
|
@@ -203,10 +220,13 @@ async function transformResource(register, fhirSchema) {
|
|
|
203
220
|
let base;
|
|
204
221
|
if (fhirSchema.base && fhirSchema.type !== "Element") {
|
|
205
222
|
const baseFs = register.resolveFs(register.ensureCanonicalUrl(fhirSchema.base));
|
|
223
|
+
if (!baseFs) {
|
|
224
|
+
throw new Error(`Base resource not found '${fhirSchema.base}' for '${fhirSchema.url}'`);
|
|
225
|
+
}
|
|
206
226
|
base = mkIdentifier(baseFs);
|
|
207
227
|
}
|
|
208
|
-
const fields = await
|
|
209
|
-
const nested = await
|
|
228
|
+
const fields = await mkFields(register, fhirSchema, [], fhirSchema.elements);
|
|
229
|
+
const nested = await mkNestedTypes(register, fhirSchema);
|
|
210
230
|
const dependencies = extractDependencies(identifier, base, fields, nested);
|
|
211
231
|
const typeSchema = {
|
|
212
232
|
identifier,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* This file is deprecated as profiles are not part of the core TypeSchema specification
|
|
6
6
|
*/
|
|
7
7
|
import { mkIdentifier } from "../core/identifier";
|
|
8
|
-
import {
|
|
8
|
+
import { mkFields } from "../core/transformer";
|
|
9
9
|
/**
|
|
10
10
|
* Transform a FHIR profile to TypeSchema format
|
|
11
11
|
* Profiles are treated as specialized resources that extend base resources
|
|
@@ -54,7 +54,7 @@ export async function transformProfile(register, fhirSchema) {
|
|
|
54
54
|
}
|
|
55
55
|
// Process profile fields from differential elements
|
|
56
56
|
if (fhirSchema.elements) {
|
|
57
|
-
const fields = await
|
|
57
|
+
const fields = await mkFields(register, fhirSchema, [], fhirSchema.elements);
|
|
58
58
|
if (fields && Object.keys(fields).length > 0) {
|
|
59
59
|
profileSchema.fields = fields;
|
|
60
60
|
}
|
|
@@ -79,10 +79,12 @@ export async function transformProfile(register, fhirSchema) {
|
|
|
79
79
|
/**
|
|
80
80
|
* Determine the kind of the base type for a profile
|
|
81
81
|
*/
|
|
82
|
-
async function determineBaseKind(baseUrl,
|
|
82
|
+
async function determineBaseKind(baseUrl, register) {
|
|
83
83
|
try {
|
|
84
84
|
// Try to resolve the base schema
|
|
85
|
-
const baseSchema =
|
|
85
|
+
const baseSchema = register.resolveFs(baseUrl);
|
|
86
|
+
if (!baseSchema)
|
|
87
|
+
return "resource";
|
|
86
88
|
if (baseSchema) {
|
|
87
89
|
// If it's also a constraint, it's likely another profile
|
|
88
90
|
if (baseSchema.derivation === "constraint") {
|
|
@@ -11,7 +11,9 @@ export type Register = {
|
|
|
11
11
|
allSd(): StructureDefinition[];
|
|
12
12
|
allFs(): RichFHIRSchema[];
|
|
13
13
|
allVs(): any[];
|
|
14
|
+
resolveVs(canonicalUrl: CanonicalUrl): any | undefined;
|
|
14
15
|
complexTypeDict(): Record<string, RichFHIRSchema>;
|
|
16
|
+
resolveAny(canonicalUrl: CanonicalUrl): any | undefined;
|
|
15
17
|
} & ReturnType<typeof CanonicalManager>;
|
|
16
18
|
export declare const registerFromManager: (manager: ReturnType<typeof CanonicalManager>, logger?: CodegenLogger, packageInfo?: PackageMeta) => Promise<Register>;
|
|
17
19
|
export declare const registerFromPackageMetas: (packageMetas: PackageMeta[], logger?: CodegenLogger) => Promise<Register>;
|
|
@@ -3,6 +3,13 @@ import * as fhirschema from "@atomic-ehr/fhirschema";
|
|
|
3
3
|
import { enrichFHIRSchema } from "@typeschema/types";
|
|
4
4
|
export const registerFromManager = async (manager, logger, packageInfo) => {
|
|
5
5
|
const resources = await manager.search({});
|
|
6
|
+
const any = {};
|
|
7
|
+
for (const resource of resources) {
|
|
8
|
+
const url = resource.url;
|
|
9
|
+
if (!url)
|
|
10
|
+
continue;
|
|
11
|
+
any[url] = resource;
|
|
12
|
+
}
|
|
6
13
|
const structureDefinitions = {};
|
|
7
14
|
for (const resource of resources) {
|
|
8
15
|
if (resource.resourceType === "StructureDefinition") {
|
|
@@ -41,14 +48,13 @@ export const registerFromManager = async (manager, logger, packageInfo) => {
|
|
|
41
48
|
const resolveFsGenealogy = (canonicalUrl) => {
|
|
42
49
|
let fs = fhirSchemas[canonicalUrl];
|
|
43
50
|
if (fs === undefined)
|
|
44
|
-
throw new Error(`Failed to resolve FHIR Schema genealogy for ${canonicalUrl}`);
|
|
51
|
+
throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
|
|
45
52
|
const genealogy = [fs];
|
|
46
53
|
while (fs?.base) {
|
|
47
|
-
console.log(1, fs.base);
|
|
48
54
|
fs = fhirSchemas[fs.base] || fhirSchemas[nameDict[fs.base]];
|
|
49
55
|
genealogy.push(fs);
|
|
50
56
|
if (fs === undefined)
|
|
51
|
-
throw new Error(`Failed to resolve FHIR Schema genealogy for ${canonicalUrl}`);
|
|
57
|
+
throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
|
|
52
58
|
}
|
|
53
59
|
return genealogy;
|
|
54
60
|
};
|
|
@@ -57,6 +63,7 @@ export const registerFromManager = async (manager, logger, packageInfo) => {
|
|
|
57
63
|
appendFs(fs) {
|
|
58
64
|
const rfs = enrichFHIRSchema(fs);
|
|
59
65
|
fhirSchemas[rfs.url] = rfs;
|
|
66
|
+
nameDict[rfs.name] = rfs.url;
|
|
60
67
|
},
|
|
61
68
|
resolveFs: (canonicalUrl) => fhirSchemas[canonicalUrl],
|
|
62
69
|
resolveFsGenealogy: resolveFsGenealogy,
|
|
@@ -65,7 +72,9 @@ export const registerFromManager = async (manager, logger, packageInfo) => {
|
|
|
65
72
|
resolveSd: (canonicalUrl) => structureDefinitions[canonicalUrl],
|
|
66
73
|
allFs: () => Object.values(fhirSchemas),
|
|
67
74
|
allVs: () => Object.values(valueSets),
|
|
75
|
+
resolveVs: (canonicalUrl) => valueSets[canonicalUrl],
|
|
68
76
|
complexTypeDict: () => complexTypes,
|
|
77
|
+
resolveAny: (canonicalUrl) => any[canonicalUrl],
|
|
69
78
|
};
|
|
70
79
|
};
|
|
71
80
|
export const registerFromPackageMetas = async (packageMetas, logger) => {
|
|
@@ -97,5 +106,8 @@ export const resolveFsElementGenealogy = (genealogy, path) => {
|
|
|
97
106
|
};
|
|
98
107
|
export function fsElementSnapshot(genealogy) {
|
|
99
108
|
// FIXME: nested elements will break it
|
|
100
|
-
|
|
109
|
+
const snapshot = genealogy.reverse().reduce((snapshot, elem) => ({ ...snapshot, ...elem }), {});
|
|
110
|
+
// NOTE: to avoid regeneration nested types
|
|
111
|
+
snapshot.elements = undefined;
|
|
112
|
+
return snapshot;
|
|
101
113
|
}
|