@atomic-ehr/codegen 0.0.1-canary.20250821160126.c552195 → 0.0.1-canary.20250822150706.c3b8669
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/builder.d.ts +3 -3
- package/dist/api/builder.d.ts.map +1 -1
- package/dist/api/builder.js +374 -0
- package/dist/api/generators/base/BaseGenerator.d.ts +4 -4
- package/dist/api/generators/base/BaseGenerator.d.ts.map +1 -1
- package/dist/api/generators/base/BaseGenerator.js +572 -0
- package/dist/api/generators/base/FileManager.d.ts +2 -2
- package/dist/api/generators/base/FileManager.d.ts.map +1 -1
- package/dist/api/generators/base/FileManager.js +204 -0
- package/dist/api/generators/base/PythonTypeMapper.d.ts +2 -2
- package/dist/api/generators/base/PythonTypeMapper.d.ts.map +1 -1
- package/dist/api/generators/base/PythonTypeMapper.js +71 -0
- package/dist/api/generators/base/TemplateEngine.d.ts +1 -1
- package/dist/api/generators/base/TemplateEngine.d.ts.map +1 -1
- package/dist/api/generators/base/TemplateEngine.js +133 -0
- package/dist/api/generators/base/TypeMapper.js +153 -0
- package/dist/api/generators/base/TypeScriptTypeMapper.d.ts +1 -1
- package/dist/api/generators/base/TypeScriptTypeMapper.d.ts.map +1 -1
- package/dist/api/generators/base/TypeScriptTypeMapper.js +232 -0
- package/dist/api/generators/base/builders/DirectoryBuilder.d.ts +4 -4
- package/dist/api/generators/base/builders/DirectoryBuilder.d.ts.map +1 -1
- package/dist/api/generators/base/builders/DirectoryBuilder.js +215 -0
- package/dist/api/generators/base/builders/FileBuilder.d.ts +2 -2
- package/dist/api/generators/base/builders/FileBuilder.d.ts.map +1 -1
- package/dist/api/generators/base/builders/FileBuilder.js +408 -0
- package/dist/api/generators/base/builders/IndexBuilder.d.ts +2 -2
- package/dist/api/generators/base/builders/IndexBuilder.d.ts.map +1 -1
- package/dist/api/generators/base/builders/IndexBuilder.js +290 -0
- package/dist/api/generators/base/enhanced-errors.d.ts +2 -2
- package/dist/api/generators/base/enhanced-errors.d.ts.map +1 -1
- package/dist/api/generators/base/enhanced-errors.js +259 -0
- package/dist/api/generators/base/error-handler.d.ts +1 -1
- package/dist/api/generators/base/error-handler.d.ts.map +1 -1
- package/dist/api/generators/base/error-handler.js +243 -0
- package/dist/api/generators/base/errors.d.ts +2 -2
- package/dist/api/generators/base/errors.d.ts.map +1 -1
- package/dist/api/generators/base/errors.js +694 -0
- package/dist/api/generators/base/index.d.ts +22 -22
- package/dist/api/generators/base/index.d.ts.map +1 -1
- package/dist/api/generators/base/index.js +161 -0
- package/dist/api/generators/base/types.d.ts +2 -2
- package/dist/api/generators/base/types.d.ts.map +1 -1
- package/dist/api/generators/base/types.js +12 -0
- package/dist/api/generators/rest-client.d.ts +2 -2
- package/dist/api/generators/rest-client.d.ts.map +1 -1
- package/dist/api/generators/rest-client.js +847 -0
- package/dist/api/generators/search-parameter-enhancer.d.ts +1 -1
- package/dist/api/generators/search-parameter-enhancer.d.ts.map +1 -1
- package/dist/api/generators/search-parameter-enhancer.js +801 -0
- package/dist/api/generators/types.js +4 -0
- package/dist/api/generators/typescript.d.ts +3 -3
- package/dist/api/generators/typescript.d.ts.map +1 -1
- package/dist/api/generators/typescript.js +537 -0
- package/dist/api/generators/validation-generator.js +632 -0
- package/dist/api/index.d.ts +10 -10
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +51 -0
- package/dist/cli/commands/generate/typescript.d.ts +1 -1
- package/dist/cli/commands/generate/typescript.d.ts.map +1 -1
- package/dist/cli/commands/generate/typescript.js +52 -0
- package/dist/cli/commands/generate.d.ts +5 -12
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +158 -0
- package/dist/cli/commands/index.d.ts +2 -1
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +100 -0
- package/dist/cli/commands/typeschema/generate.js +130 -0
- package/dist/cli/commands/typeschema.js +48 -0
- package/dist/cli/index.js +12 -8664
- package/dist/cli/utils/log.d.ts +2 -2
- package/dist/cli/utils/log.d.ts.map +1 -1
- package/dist/cli/utils/log.js +23 -0
- package/dist/cli/utils/prompts.js +224 -0
- package/dist/cli/utils/spinner.js +270 -0
- package/dist/config.d.ts +22 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +703 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +84 -38
- package/dist/logger.js +290 -0
- package/dist/typeschema/cache.d.ts +2 -2
- package/dist/typeschema/cache.d.ts.map +1 -1
- package/dist/typeschema/cache.js +285 -0
- package/dist/typeschema/core/binding.d.ts +1 -1
- package/dist/typeschema/core/binding.d.ts.map +1 -1
- package/dist/typeschema/core/binding.js +187 -0
- package/dist/typeschema/core/field-builder.d.ts +1 -1
- package/dist/typeschema/core/field-builder.d.ts.map +1 -1
- package/dist/typeschema/core/field-builder.js +259 -0
- package/dist/typeschema/core/identifier.js +117 -0
- package/dist/typeschema/core/nested-types.d.ts +1 -1
- package/dist/typeschema/core/nested-types.d.ts.map +1 -1
- package/dist/typeschema/core/nested-types.js +111 -0
- package/dist/typeschema/core/transformer.d.ts +2 -2
- package/dist/typeschema/core/transformer.d.ts.map +1 -1
- package/dist/typeschema/core/transformer.js +345 -0
- package/dist/typeschema/generator.d.ts +3 -3
- package/dist/typeschema/generator.d.ts.map +1 -1
- package/dist/typeschema/generator.js +352 -0
- package/dist/typeschema/index.d.ts +14 -14
- package/dist/typeschema/index.d.ts.map +1 -1
- package/dist/typeschema/index.js +92 -0
- package/dist/typeschema/parser.d.ts +2 -2
- package/dist/typeschema/parser.d.ts.map +1 -1
- package/dist/typeschema/parser.js +310 -0
- package/dist/typeschema/profile/processor.d.ts +1 -1
- package/dist/typeschema/profile/processor.d.ts.map +1 -1
- package/dist/typeschema/profile/processor.js +268 -0
- package/dist/typeschema/schema.js +456 -0
- package/dist/typeschema/type-schema.types.js +39 -0
- package/dist/typeschema/types.js +4 -0
- package/dist/typeschema/utils.d.ts +1 -1
- package/dist/typeschema/utils.d.ts.map +1 -1
- package/dist/typeschema/utils.js +13 -0
- package/dist/typeschema/value-set/processor.d.ts +1 -1
- package/dist/typeschema/value-set/processor.d.ts.map +1 -1
- package/dist/typeschema/value-set/processor.js +168 -0
- package/dist/utils/codegen-logger.js +204 -0
- package/dist/utils.js +42 -0
- package/package.json +15 -4
- package/dist/index-e7pfye24.js +0 -8532
|
@@ -0,0 +1,801 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search Parameter Enhancer
|
|
3
|
+
*
|
|
4
|
+
* Generates enhanced search parameter types and interfaces for FHIR resources.
|
|
5
|
+
* Provides better type safety, validation, and developer experience for search operations.
|
|
6
|
+
*/
|
|
7
|
+
import { createLogger } from "../../utils/codegen-logger.js";
|
|
8
|
+
/**
|
|
9
|
+
* Search Parameter Enhancer class
|
|
10
|
+
*
|
|
11
|
+
* Generates enhanced type-safe search parameters and validation helpers
|
|
12
|
+
* for FHIR REST client operations.
|
|
13
|
+
*/
|
|
14
|
+
export class SearchParameterEnhancer {
|
|
15
|
+
resourceTypes = new Set();
|
|
16
|
+
resourceSearchParams = new Map();
|
|
17
|
+
autocompleteEnabled;
|
|
18
|
+
valueSetEnumsEnabled;
|
|
19
|
+
logger;
|
|
20
|
+
availableEnumTypes = new Map();
|
|
21
|
+
static BASE_PARAM_NAMES = [
|
|
22
|
+
"_count",
|
|
23
|
+
"_offset",
|
|
24
|
+
"_sort",
|
|
25
|
+
"_summary",
|
|
26
|
+
"_elements",
|
|
27
|
+
"_lastUpdated",
|
|
28
|
+
"_profile",
|
|
29
|
+
"_security",
|
|
30
|
+
"_tag",
|
|
31
|
+
"_id",
|
|
32
|
+
"_text",
|
|
33
|
+
"_content",
|
|
34
|
+
];
|
|
35
|
+
constructor(options) {
|
|
36
|
+
this.autocompleteEnabled = !!options?.autocomplete;
|
|
37
|
+
this.valueSetEnumsEnabled = !!options?.valueSetEnums;
|
|
38
|
+
this.logger = options?.logger || createLogger({ prefix: "SearchParam" });
|
|
39
|
+
this.logger.debug(`SearchParameterEnhancer initialized: autocomplete=${this.autocompleteEnabled}, valueSetEnums=${this.valueSetEnumsEnabled}`);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Generate per-resource SearchParamName unions (for IDE autocomplete)
|
|
43
|
+
*/
|
|
44
|
+
generateSearchParamNameUnions() {
|
|
45
|
+
const baseUnion = SearchParameterEnhancer.BASE_PARAM_NAMES.map((n) => `'${n}'`).join(" | ");
|
|
46
|
+
const parts = [];
|
|
47
|
+
parts.push(`/**\n * Base search parameter names available for all resources\n */`);
|
|
48
|
+
parts.push(`export type BaseSearchParamName = ${baseUnion};`);
|
|
49
|
+
parts.push("");
|
|
50
|
+
const resourceTypesArray = Array.from(this.resourceTypes).sort();
|
|
51
|
+
for (const [resourceType, params] of this.resourceSearchParams.entries()) {
|
|
52
|
+
const specificNames = Array.from(new Set(params.map((p) => p.name))).sort();
|
|
53
|
+
const specificUnion = specificNames.map((n) => `'${n}'`).join(" | ");
|
|
54
|
+
const typeName = `${resourceType}SearchParamName`;
|
|
55
|
+
if (specificUnion.length > 0) {
|
|
56
|
+
parts.push(`/**\n * Search parameter names for ${resourceType}\n */`);
|
|
57
|
+
parts.push(`export type ${typeName} = BaseSearchParamName | ${specificUnion};`);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
parts.push(`export type ${typeName} = BaseSearchParamName;`);
|
|
61
|
+
}
|
|
62
|
+
parts.push("");
|
|
63
|
+
}
|
|
64
|
+
// Generic mapping type
|
|
65
|
+
const mappingLines = resourceTypesArray
|
|
66
|
+
.map((t) => `\tT extends '${t}' ? ${t}SearchParamName :`)
|
|
67
|
+
.join("\n");
|
|
68
|
+
parts.push(`/**\n * Generic search parameter name union for a given resource type\n */`);
|
|
69
|
+
parts.push(`export type SearchParamName<T extends ResourceTypes> =\n${mappingLines}\n\tBaseSearchParamName;`);
|
|
70
|
+
return parts.join("\n");
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Collect resource types and their search parameters from schemas
|
|
74
|
+
*/
|
|
75
|
+
collectResourceData(schemas) {
|
|
76
|
+
this.resourceTypes.clear();
|
|
77
|
+
this.resourceSearchParams.clear();
|
|
78
|
+
for (const schema of schemas) {
|
|
79
|
+
if (schema.identifier.kind === "resource" &&
|
|
80
|
+
schema.identifier.name !== "DomainResource" &&
|
|
81
|
+
schema.identifier.name !== "Resource") {
|
|
82
|
+
this.resourceTypes.add(schema.identifier.name);
|
|
83
|
+
this.collectSearchParameters(schema);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Collect search parameters for a specific resource type
|
|
89
|
+
*/
|
|
90
|
+
collectSearchParameters(schema) {
|
|
91
|
+
const resourceType = schema.identifier.name;
|
|
92
|
+
const searchParams = [];
|
|
93
|
+
// Add common search parameters based on FHIR specification
|
|
94
|
+
this.addCommonSearchParameters(resourceType, searchParams);
|
|
95
|
+
this.resourceSearchParams.set(resourceType, searchParams);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Add common search parameters based on resource type
|
|
99
|
+
*/
|
|
100
|
+
addCommonSearchParameters(resourceType, searchParams) {
|
|
101
|
+
// Add resource-specific search parameters based on FHIR R4 specification
|
|
102
|
+
switch (resourceType) {
|
|
103
|
+
case "Patient":
|
|
104
|
+
searchParams.push({
|
|
105
|
+
name: "active",
|
|
106
|
+
type: "token",
|
|
107
|
+
description: "Whether the patient record is active",
|
|
108
|
+
}, {
|
|
109
|
+
name: "address",
|
|
110
|
+
type: "string",
|
|
111
|
+
description: "A server defined search that may match any of the string fields in the Address",
|
|
112
|
+
}, {
|
|
113
|
+
name: "address-city",
|
|
114
|
+
type: "string",
|
|
115
|
+
description: "A city specified in an address",
|
|
116
|
+
}, {
|
|
117
|
+
name: "address-country",
|
|
118
|
+
type: "string",
|
|
119
|
+
description: "A country specified in an address",
|
|
120
|
+
}, {
|
|
121
|
+
name: "address-postalcode",
|
|
122
|
+
type: "string",
|
|
123
|
+
description: "A postalCode specified in an address",
|
|
124
|
+
}, {
|
|
125
|
+
name: "address-state",
|
|
126
|
+
type: "string",
|
|
127
|
+
description: "A state specified in an address",
|
|
128
|
+
}, {
|
|
129
|
+
name: "address-use",
|
|
130
|
+
type: "token",
|
|
131
|
+
description: "A use code specified in an address",
|
|
132
|
+
}, {
|
|
133
|
+
name: "birthdate",
|
|
134
|
+
type: "date",
|
|
135
|
+
description: "The patient's date of birth",
|
|
136
|
+
}, {
|
|
137
|
+
name: "death-date",
|
|
138
|
+
type: "date",
|
|
139
|
+
description: "The date of death has been provided and satisfies this search value",
|
|
140
|
+
}, {
|
|
141
|
+
name: "deceased",
|
|
142
|
+
type: "token",
|
|
143
|
+
description: "This patient has been marked as deceased, or as a death date entered",
|
|
144
|
+
}, {
|
|
145
|
+
name: "email",
|
|
146
|
+
type: "token",
|
|
147
|
+
description: "A value in an email contact",
|
|
148
|
+
}, {
|
|
149
|
+
name: "family",
|
|
150
|
+
type: "string",
|
|
151
|
+
description: "A portion of the family name of the patient",
|
|
152
|
+
}, {
|
|
153
|
+
name: "gender",
|
|
154
|
+
type: "token",
|
|
155
|
+
description: "Gender of the patient",
|
|
156
|
+
}, {
|
|
157
|
+
name: "general-practitioner",
|
|
158
|
+
type: "reference",
|
|
159
|
+
target: ["Organization", "Practitioner", "PractitionerRole"],
|
|
160
|
+
description: "Patient's nominated general practitioner",
|
|
161
|
+
}, {
|
|
162
|
+
name: "given",
|
|
163
|
+
type: "string",
|
|
164
|
+
description: "A portion of the given name of the patient",
|
|
165
|
+
}, {
|
|
166
|
+
name: "identifier",
|
|
167
|
+
type: "token",
|
|
168
|
+
description: "A patient identifier",
|
|
169
|
+
}, {
|
|
170
|
+
name: "language",
|
|
171
|
+
type: "token",
|
|
172
|
+
description: "Language code (irrespective of use value)",
|
|
173
|
+
}, {
|
|
174
|
+
name: "link",
|
|
175
|
+
type: "reference",
|
|
176
|
+
target: ["Patient", "RelatedPerson"],
|
|
177
|
+
description: "All patients linked to the given patient",
|
|
178
|
+
}, {
|
|
179
|
+
name: "name",
|
|
180
|
+
type: "string",
|
|
181
|
+
description: "A server defined search that may match any of the string fields in the HumanName",
|
|
182
|
+
}, {
|
|
183
|
+
name: "organization",
|
|
184
|
+
type: "reference",
|
|
185
|
+
target: ["Organization"],
|
|
186
|
+
description: "The organization that is the custodian of the patient record",
|
|
187
|
+
}, {
|
|
188
|
+
name: "phone",
|
|
189
|
+
type: "token",
|
|
190
|
+
description: "A value in a phone contact",
|
|
191
|
+
}, {
|
|
192
|
+
name: "phonetic",
|
|
193
|
+
type: "string",
|
|
194
|
+
description: "A portion of either family or given name using some kind of phonetic matching algorithm",
|
|
195
|
+
}, {
|
|
196
|
+
name: "telecom",
|
|
197
|
+
type: "token",
|
|
198
|
+
description: "The value in any kind of telecom details of the patient",
|
|
199
|
+
});
|
|
200
|
+
break;
|
|
201
|
+
case "Observation":
|
|
202
|
+
searchParams.push({
|
|
203
|
+
name: "category",
|
|
204
|
+
type: "token",
|
|
205
|
+
description: "The classification of the type of observation",
|
|
206
|
+
}, {
|
|
207
|
+
name: "code",
|
|
208
|
+
type: "token",
|
|
209
|
+
description: "The code of the observation type",
|
|
210
|
+
}, {
|
|
211
|
+
name: "component-code",
|
|
212
|
+
type: "token",
|
|
213
|
+
description: "The component code of the observation type",
|
|
214
|
+
}, {
|
|
215
|
+
name: "component-data-absent-reason",
|
|
216
|
+
type: "token",
|
|
217
|
+
description: "The reason why the expected value in the element Observation.component.value[x] is missing",
|
|
218
|
+
}, {
|
|
219
|
+
name: "component-value-concept",
|
|
220
|
+
type: "token",
|
|
221
|
+
description: "The value of the component observation, if the value is a CodeableConcept",
|
|
222
|
+
}, {
|
|
223
|
+
name: "component-value-quantity",
|
|
224
|
+
type: "quantity",
|
|
225
|
+
description: "The value of the component observation, if the value is a Quantity, or a SampledData",
|
|
226
|
+
}, {
|
|
227
|
+
name: "data-absent-reason",
|
|
228
|
+
type: "token",
|
|
229
|
+
description: "The reason why the expected value in the element Observation.value[x] is missing",
|
|
230
|
+
}, {
|
|
231
|
+
name: "date",
|
|
232
|
+
type: "date",
|
|
233
|
+
description: "Obtained date/time. If the obtained element is a period, a date that falls in the period",
|
|
234
|
+
}, {
|
|
235
|
+
name: "derived-from",
|
|
236
|
+
type: "reference",
|
|
237
|
+
target: [
|
|
238
|
+
"DocumentReference",
|
|
239
|
+
"ImagingStudy",
|
|
240
|
+
"Media",
|
|
241
|
+
"QuestionnaireResponse",
|
|
242
|
+
"Observation",
|
|
243
|
+
"MolecularSequence",
|
|
244
|
+
],
|
|
245
|
+
description: "Related measurements the observation is made from",
|
|
246
|
+
}, {
|
|
247
|
+
name: "device",
|
|
248
|
+
type: "reference",
|
|
249
|
+
target: ["Device", "DeviceMetric"],
|
|
250
|
+
description: "The Device that generated the observation data",
|
|
251
|
+
}, {
|
|
252
|
+
name: "encounter",
|
|
253
|
+
type: "reference",
|
|
254
|
+
target: ["Encounter"],
|
|
255
|
+
description: "Encounter related to the observation",
|
|
256
|
+
}, {
|
|
257
|
+
name: "focus",
|
|
258
|
+
type: "reference",
|
|
259
|
+
target: ["Resource"],
|
|
260
|
+
description: "The focus of an observation when the focus is not the patient of record",
|
|
261
|
+
}, {
|
|
262
|
+
name: "has-member",
|
|
263
|
+
type: "reference",
|
|
264
|
+
target: [
|
|
265
|
+
"Observation",
|
|
266
|
+
"QuestionnaireResponse",
|
|
267
|
+
"MolecularSequence",
|
|
268
|
+
],
|
|
269
|
+
description: "Related resource that belongs to the Observation group",
|
|
270
|
+
}, {
|
|
271
|
+
name: "identifier",
|
|
272
|
+
type: "token",
|
|
273
|
+
description: "The unique id for a particular observation",
|
|
274
|
+
}, {
|
|
275
|
+
name: "method",
|
|
276
|
+
type: "token",
|
|
277
|
+
description: "The method used for the observation",
|
|
278
|
+
}, {
|
|
279
|
+
name: "part-of",
|
|
280
|
+
type: "reference",
|
|
281
|
+
target: [
|
|
282
|
+
"MedicationAdministration",
|
|
283
|
+
"MedicationDispense",
|
|
284
|
+
"MedicationStatement",
|
|
285
|
+
"Procedure",
|
|
286
|
+
"Immunization",
|
|
287
|
+
"ImagingStudy",
|
|
288
|
+
],
|
|
289
|
+
description: "Part of referenced event",
|
|
290
|
+
}, {
|
|
291
|
+
name: "patient",
|
|
292
|
+
type: "reference",
|
|
293
|
+
target: ["Patient"],
|
|
294
|
+
description: "The subject that the observation is about (if patient)",
|
|
295
|
+
}, {
|
|
296
|
+
name: "performer",
|
|
297
|
+
type: "reference",
|
|
298
|
+
target: [
|
|
299
|
+
"Practitioner",
|
|
300
|
+
"PractitionerRole",
|
|
301
|
+
"Organization",
|
|
302
|
+
"CareTeam",
|
|
303
|
+
"Patient",
|
|
304
|
+
"RelatedPerson",
|
|
305
|
+
],
|
|
306
|
+
description: "Who performed the observation",
|
|
307
|
+
}, {
|
|
308
|
+
name: "specimen",
|
|
309
|
+
type: "reference",
|
|
310
|
+
target: ["Specimen"],
|
|
311
|
+
description: "Specimen used for this observation",
|
|
312
|
+
}, {
|
|
313
|
+
name: "status",
|
|
314
|
+
type: "token",
|
|
315
|
+
description: "The status of the observation",
|
|
316
|
+
}, {
|
|
317
|
+
name: "subject",
|
|
318
|
+
type: "reference",
|
|
319
|
+
target: ["Patient", "Group", "Device", "Location"],
|
|
320
|
+
description: "The subject that the observation is about",
|
|
321
|
+
}, {
|
|
322
|
+
name: "value-concept",
|
|
323
|
+
type: "token",
|
|
324
|
+
description: "The value of the observation, if the value is a CodeableConcept",
|
|
325
|
+
}, {
|
|
326
|
+
name: "value-date",
|
|
327
|
+
type: "date",
|
|
328
|
+
description: "The value of the observation, if the value is a date or period of time",
|
|
329
|
+
}, {
|
|
330
|
+
name: "value-quantity",
|
|
331
|
+
type: "quantity",
|
|
332
|
+
description: "The value of the observation, if the value is a Quantity, or a SampledData",
|
|
333
|
+
}, {
|
|
334
|
+
name: "value-string",
|
|
335
|
+
type: "string",
|
|
336
|
+
description: "The value of the observation, if the value is a string, and also searches in CodeableConcept.text",
|
|
337
|
+
});
|
|
338
|
+
break;
|
|
339
|
+
case "Organization":
|
|
340
|
+
searchParams.push({
|
|
341
|
+
name: "active",
|
|
342
|
+
type: "token",
|
|
343
|
+
description: "Is the Organization record active",
|
|
344
|
+
}, {
|
|
345
|
+
name: "address",
|
|
346
|
+
type: "string",
|
|
347
|
+
description: "A server defined search that may match any of the string fields in the Address",
|
|
348
|
+
}, {
|
|
349
|
+
name: "address-city",
|
|
350
|
+
type: "string",
|
|
351
|
+
description: "A city specified in an address",
|
|
352
|
+
}, {
|
|
353
|
+
name: "address-country",
|
|
354
|
+
type: "string",
|
|
355
|
+
description: "A country specified in an address",
|
|
356
|
+
}, {
|
|
357
|
+
name: "address-postalcode",
|
|
358
|
+
type: "string",
|
|
359
|
+
description: "A postal code specified in an address",
|
|
360
|
+
}, {
|
|
361
|
+
name: "address-state",
|
|
362
|
+
type: "string",
|
|
363
|
+
description: "A state specified in an address",
|
|
364
|
+
}, {
|
|
365
|
+
name: "address-use",
|
|
366
|
+
type: "token",
|
|
367
|
+
description: "A use code specified in an address",
|
|
368
|
+
}, {
|
|
369
|
+
name: "endpoint",
|
|
370
|
+
type: "reference",
|
|
371
|
+
target: ["Endpoint"],
|
|
372
|
+
description: "Technical endpoints providing access to services operated for the organization",
|
|
373
|
+
}, {
|
|
374
|
+
name: "identifier",
|
|
375
|
+
type: "token",
|
|
376
|
+
description: "Any identifier for the organization (not the accreditation issuer's identifier)",
|
|
377
|
+
}, {
|
|
378
|
+
name: "name",
|
|
379
|
+
type: "string",
|
|
380
|
+
description: "A portion of the organization's name or alias",
|
|
381
|
+
}, {
|
|
382
|
+
name: "partof",
|
|
383
|
+
type: "reference",
|
|
384
|
+
target: ["Organization"],
|
|
385
|
+
description: "An organization of which this organization forms a part",
|
|
386
|
+
}, {
|
|
387
|
+
name: "phonetic",
|
|
388
|
+
type: "string",
|
|
389
|
+
description: "A portion of the organization's name using some kind of phonetic matching algorithm",
|
|
390
|
+
}, {
|
|
391
|
+
name: "type",
|
|
392
|
+
type: "token",
|
|
393
|
+
description: "A code for the type of organization",
|
|
394
|
+
});
|
|
395
|
+
break;
|
|
396
|
+
case "Practitioner":
|
|
397
|
+
searchParams.push({
|
|
398
|
+
name: "active",
|
|
399
|
+
type: "token",
|
|
400
|
+
description: "Whether the practitioner record is active",
|
|
401
|
+
}, {
|
|
402
|
+
name: "address",
|
|
403
|
+
type: "string",
|
|
404
|
+
description: "A server defined search that may match any of the string fields in the Address",
|
|
405
|
+
}, {
|
|
406
|
+
name: "address-city",
|
|
407
|
+
type: "string",
|
|
408
|
+
description: "A city specified in an address",
|
|
409
|
+
}, {
|
|
410
|
+
name: "address-country",
|
|
411
|
+
type: "string",
|
|
412
|
+
description: "A country specified in an address",
|
|
413
|
+
}, {
|
|
414
|
+
name: "address-postalcode",
|
|
415
|
+
type: "string",
|
|
416
|
+
description: "A postal code specified in an address",
|
|
417
|
+
}, {
|
|
418
|
+
name: "address-state",
|
|
419
|
+
type: "string",
|
|
420
|
+
description: "A state specified in an address",
|
|
421
|
+
}, {
|
|
422
|
+
name: "address-use",
|
|
423
|
+
type: "token",
|
|
424
|
+
description: "A use code specified in an address",
|
|
425
|
+
}, {
|
|
426
|
+
name: "communication",
|
|
427
|
+
type: "token",
|
|
428
|
+
description: "One of the languages that the practitioner can communicate with",
|
|
429
|
+
}, {
|
|
430
|
+
name: "email",
|
|
431
|
+
type: "token",
|
|
432
|
+
description: "A value in an email contact",
|
|
433
|
+
}, {
|
|
434
|
+
name: "family",
|
|
435
|
+
type: "string",
|
|
436
|
+
description: "A portion of the family name",
|
|
437
|
+
}, {
|
|
438
|
+
name: "gender",
|
|
439
|
+
type: "token",
|
|
440
|
+
description: "Gender of the practitioner",
|
|
441
|
+
}, {
|
|
442
|
+
name: "given",
|
|
443
|
+
type: "string",
|
|
444
|
+
description: "A portion of the given name",
|
|
445
|
+
}, {
|
|
446
|
+
name: "identifier",
|
|
447
|
+
type: "token",
|
|
448
|
+
description: "A practitioner's Identifier",
|
|
449
|
+
}, {
|
|
450
|
+
name: "name",
|
|
451
|
+
type: "string",
|
|
452
|
+
description: "A server defined search that may match any of the string fields in the HumanName",
|
|
453
|
+
}, {
|
|
454
|
+
name: "phone",
|
|
455
|
+
type: "token",
|
|
456
|
+
description: "A value in a phone contact",
|
|
457
|
+
}, {
|
|
458
|
+
name: "phonetic",
|
|
459
|
+
type: "string",
|
|
460
|
+
description: "A portion of either family or given name using some kind of phonetic matching algorithm",
|
|
461
|
+
}, {
|
|
462
|
+
name: "telecom",
|
|
463
|
+
type: "token",
|
|
464
|
+
description: "The value in any kind of contact",
|
|
465
|
+
});
|
|
466
|
+
break;
|
|
467
|
+
default:
|
|
468
|
+
// Add common search parameters for all resources
|
|
469
|
+
searchParams.push({
|
|
470
|
+
name: "identifier",
|
|
471
|
+
type: "token",
|
|
472
|
+
description: "Resource identifier",
|
|
473
|
+
});
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Pre-populate enum types by processing all search parameters
|
|
479
|
+
*/
|
|
480
|
+
preprocessEnumTypes() {
|
|
481
|
+
// Clear any existing enum types
|
|
482
|
+
this.availableEnumTypes.clear();
|
|
483
|
+
// Process all search parameters to populate enum types
|
|
484
|
+
for (const [resourceType, searchParams,] of this.resourceSearchParams.entries()) {
|
|
485
|
+
for (const param of searchParams) {
|
|
486
|
+
if (param.type === "token" && this.valueSetEnumsEnabled) {
|
|
487
|
+
// Generate enum type name based on FHIR naming convention: ResourceFieldValues
|
|
488
|
+
const enumTypeName = `${resourceType}${this.toPascalCase(param.name)}Values`;
|
|
489
|
+
// Add the enum type to our available types
|
|
490
|
+
// The TypeScript generator will have already created these if they exist
|
|
491
|
+
this.availableEnumTypes.set(`${resourceType}${param.name}`, enumTypeName);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Convert string to PascalCase
|
|
498
|
+
*/
|
|
499
|
+
toPascalCase(str) {
|
|
500
|
+
return str
|
|
501
|
+
.split(/[-_\s]/)
|
|
502
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
503
|
+
.join("");
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Generate enhanced search parameter types
|
|
507
|
+
*/
|
|
508
|
+
generateEnhancedSearchTypes() {
|
|
509
|
+
const resourceTypesArray = Array.from(this.resourceTypes).sort();
|
|
510
|
+
// Pre-populate enum types by processing all search parameters
|
|
511
|
+
this.preprocessEnumTypes();
|
|
512
|
+
const enumImports = this.valueSetEnumsEnabled && this.availableEnumTypes.size > 0
|
|
513
|
+
? `import type { ${Array.from(new Set(this.availableEnumTypes.values())).sort().join(", ")} } from '../types/utility';\n`
|
|
514
|
+
: "";
|
|
515
|
+
return `/**
|
|
516
|
+
* Enhanced Search Parameter Types
|
|
517
|
+
*
|
|
518
|
+
* Type-safe search parameters with modifiers and validation for FHIR resources.
|
|
519
|
+
* Generated automatically from FHIR schemas.
|
|
520
|
+
*/
|
|
521
|
+
|
|
522
|
+
import type { ResourceTypes } from '../types';
|
|
523
|
+
${enumImports}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Search parameter modifier types for enhanced type safety
|
|
527
|
+
*/
|
|
528
|
+
export interface SearchModifiers {
|
|
529
|
+
/** String search modifiers */
|
|
530
|
+
StringModifier:
|
|
531
|
+
| { exact: string }
|
|
532
|
+
| { contains: string }
|
|
533
|
+
| { missing: boolean };
|
|
534
|
+
|
|
535
|
+
/** Date parameter with prefix support */
|
|
536
|
+
DateParameter:
|
|
537
|
+
| string
|
|
538
|
+
| { gt: string }
|
|
539
|
+
| { lt: string }
|
|
540
|
+
| { ge: string }
|
|
541
|
+
| { le: string }
|
|
542
|
+
| { eq: string }
|
|
543
|
+
| { ne: string }
|
|
544
|
+
| { missing: boolean };
|
|
545
|
+
|
|
546
|
+
/** Token parameter for coded values */
|
|
547
|
+
TokenParameter:
|
|
548
|
+
| string
|
|
549
|
+
| { system: string; code: string }
|
|
550
|
+
| { code: string }
|
|
551
|
+
| { system: string }
|
|
552
|
+
| { missing: boolean };
|
|
553
|
+
|
|
554
|
+
/** Token search options (for better autocomplete when enum values exist) */
|
|
555
|
+
TokenSearchOptions:
|
|
556
|
+
| { system: string; code: string }
|
|
557
|
+
| { code: string }
|
|
558
|
+
| { system: string }
|
|
559
|
+
| { missing: boolean };
|
|
560
|
+
|
|
561
|
+
/** Reference parameter for resource references */
|
|
562
|
+
ReferenceParameter:
|
|
563
|
+
| string
|
|
564
|
+
| { reference: string }
|
|
565
|
+
| { identifier: string }
|
|
566
|
+
| { missing: boolean };
|
|
567
|
+
|
|
568
|
+
/** Number parameter with range support */
|
|
569
|
+
NumberParameter:
|
|
570
|
+
| number
|
|
571
|
+
| { gt: number }
|
|
572
|
+
| { lt: number }
|
|
573
|
+
| { ge: number }
|
|
574
|
+
| { le: number }
|
|
575
|
+
| { eq: number }
|
|
576
|
+
| { ne: number }
|
|
577
|
+
| { missing: boolean };
|
|
578
|
+
|
|
579
|
+
/** Quantity parameter for measurements */
|
|
580
|
+
QuantityParameter:
|
|
581
|
+
| number
|
|
582
|
+
| string
|
|
583
|
+
| { value: number; unit?: string; system?: string; code?: string }
|
|
584
|
+
| { missing: boolean };
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Base search parameters available for all resources
|
|
589
|
+
*/
|
|
590
|
+
export interface BaseEnhancedSearchParams {
|
|
591
|
+
/** Number of results to return */
|
|
592
|
+
_count?: number;
|
|
593
|
+
/** Pagination offset */
|
|
594
|
+
_offset?: number;
|
|
595
|
+
/** Sort order */
|
|
596
|
+
_sort?: string | string[];
|
|
597
|
+
/** Summary mode */
|
|
598
|
+
_summary?: 'true' | 'false' | 'text' | 'data' | 'count';
|
|
599
|
+
/** Elements to include */
|
|
600
|
+
_elements?: string | string[];
|
|
601
|
+
/** Filter by last updated */
|
|
602
|
+
_lastUpdated?: SearchModifiers['DateParameter'];
|
|
603
|
+
/** Profile filter */
|
|
604
|
+
_profile?: string | string[];
|
|
605
|
+
/** Security label filter */
|
|
606
|
+
_security?: string | string[];
|
|
607
|
+
/** Tag filter */
|
|
608
|
+
_tag?: SearchModifiers['TokenParameter'] | SearchModifiers['TokenParameter'][];
|
|
609
|
+
/** Filter by ID */
|
|
610
|
+
_id?: string | string[];
|
|
611
|
+
/** Text search */
|
|
612
|
+
_text?: string;
|
|
613
|
+
/** Content search */
|
|
614
|
+
_content?: string;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Enhanced search parameters union type for all resources
|
|
619
|
+
*/
|
|
620
|
+
export type EnhancedSearchParams<T extends ResourceTypes> =
|
|
621
|
+
${resourceTypesArray.map((type) => ` T extends '${type}' ? ${type}SearchParams :`).join("\n")}
|
|
622
|
+
BaseEnhancedSearchParams;
|
|
623
|
+
|
|
624
|
+
${this.generateResourceSpecificSearchInterfaces()}
|
|
625
|
+
|
|
626
|
+
${this.autocompleteEnabled ? this.generateSearchParamNameUnions() : ""}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Type-safe search parameter validation helpers
|
|
630
|
+
*/
|
|
631
|
+
export class SearchParameterValidator {
|
|
632
|
+
/**
|
|
633
|
+
* Validate search parameters for a specific resource type
|
|
634
|
+
*/
|
|
635
|
+
static validate<T extends ResourceTypes>(
|
|
636
|
+
resourceType: T,
|
|
637
|
+
params: EnhancedSearchParams<T>
|
|
638
|
+
): { valid: boolean; errors: string[] } {
|
|
639
|
+
const errors: string[] = [];
|
|
640
|
+
|
|
641
|
+
// Basic validation logic
|
|
642
|
+
if (params._count !== undefined && (params._count < 0 || params._count > 1000)) {
|
|
643
|
+
errors.push('_count must be between 0 and 1000');
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (params._offset !== undefined && params._offset < 0) {
|
|
647
|
+
errors.push('_offset must be non-negative');
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
return {
|
|
651
|
+
valid: errors.length === 0,
|
|
652
|
+
errors
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Build URL search parameters from enhanced search params
|
|
658
|
+
*/
|
|
659
|
+
static buildSearchParams<T extends ResourceTypes>(
|
|
660
|
+
resourceType: T,
|
|
661
|
+
params: EnhancedSearchParams<T>
|
|
662
|
+
): URLSearchParams {
|
|
663
|
+
const searchParams = new URLSearchParams();
|
|
664
|
+
|
|
665
|
+
for (const [key, value] of Object.entries(params)) {
|
|
666
|
+
if (value === undefined || value === null) continue;
|
|
667
|
+
|
|
668
|
+
if (Array.isArray(value)) {
|
|
669
|
+
value.forEach(v => searchParams.append(key, String(v)));
|
|
670
|
+
} else if (typeof value === 'object') {
|
|
671
|
+
// Handle complex parameter objects
|
|
672
|
+
if ('exact' in value) {
|
|
673
|
+
searchParams.append(key + ':exact', String(value.exact));
|
|
674
|
+
} else if ('contains' in value) {
|
|
675
|
+
searchParams.append(key + ':contains', String(value.contains));
|
|
676
|
+
} else if ('missing' in value) {
|
|
677
|
+
searchParams.append(key + ':missing', String(value.missing));
|
|
678
|
+
} else if ('gt' in value) {
|
|
679
|
+
searchParams.append(key + ':gt', String(value.gt));
|
|
680
|
+
} else if ('lt' in value) {
|
|
681
|
+
searchParams.append(key + ':lt', String(value.lt));
|
|
682
|
+
} else if ('ge' in value) {
|
|
683
|
+
searchParams.append(key + ':ge', String(value.ge));
|
|
684
|
+
} else if ('le' in value) {
|
|
685
|
+
searchParams.append(key + ':le', String(value.le));
|
|
686
|
+
} else if ('eq' in value) {
|
|
687
|
+
searchParams.append(key + ':eq', String(value.eq));
|
|
688
|
+
} else if ('ne' in value) {
|
|
689
|
+
searchParams.append(key + ':ne', String(value.ne));
|
|
690
|
+
} else if ('system' in value && 'code' in value) {
|
|
691
|
+
searchParams.append(key, \`\${value.system}|\${value.code}\`);
|
|
692
|
+
} else if ('system' in value) {
|
|
693
|
+
searchParams.append(key, \`\${value.system}|\`);
|
|
694
|
+
} else if ('code' in value) {
|
|
695
|
+
searchParams.append(key, \`|\${value.code}\`);
|
|
696
|
+
} else if ('reference' in value) {
|
|
697
|
+
searchParams.append(key, String(value.reference));
|
|
698
|
+
} else if ('identifier' in value) {
|
|
699
|
+
searchParams.append(key + ':identifier', String(value.identifier));
|
|
700
|
+
}
|
|
701
|
+
} else {
|
|
702
|
+
searchParams.append(key, String(value));
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
return searchParams;
|
|
707
|
+
}
|
|
708
|
+
}`;
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Generate resource-specific search parameter interfaces
|
|
712
|
+
*/
|
|
713
|
+
generateResourceSpecificSearchInterfaces() {
|
|
714
|
+
const interfaces = [];
|
|
715
|
+
for (const [resourceType, searchParams,] of this.resourceSearchParams.entries()) {
|
|
716
|
+
interfaces.push(this.generateResourceSearchInterface(resourceType, searchParams));
|
|
717
|
+
}
|
|
718
|
+
return interfaces.join("\n\n");
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Generate search interface for a specific resource type
|
|
722
|
+
*/
|
|
723
|
+
generateResourceSearchInterface(resourceType, searchParams) {
|
|
724
|
+
const interfaceFields = [];
|
|
725
|
+
// Add base search parameters
|
|
726
|
+
interfaceFields.push(" // Base search parameters");
|
|
727
|
+
interfaceFields.push(" _count?: number;");
|
|
728
|
+
interfaceFields.push(" _offset?: number;");
|
|
729
|
+
interfaceFields.push(" _sort?: string | string[];");
|
|
730
|
+
interfaceFields.push(" _summary?: 'true' | 'false' | 'text' | 'data' | 'count';");
|
|
731
|
+
interfaceFields.push(" _elements?: string | string[];");
|
|
732
|
+
interfaceFields.push(" _lastUpdated?: SearchModifiers['DateParameter'];");
|
|
733
|
+
interfaceFields.push(" _profile?: string | string[];");
|
|
734
|
+
interfaceFields.push(" _security?: string | string[];");
|
|
735
|
+
interfaceFields.push(" _tag?: SearchModifiers['TokenParameter'] | SearchModifiers['TokenParameter'][];");
|
|
736
|
+
interfaceFields.push(" _id?: string | string[];");
|
|
737
|
+
interfaceFields.push(" _text?: string;");
|
|
738
|
+
interfaceFields.push(" _content?: string;");
|
|
739
|
+
interfaceFields.push("");
|
|
740
|
+
// Add resource-specific parameters
|
|
741
|
+
if (searchParams.length > 0) {
|
|
742
|
+
interfaceFields.push(` // ${resourceType}-specific search parameters`);
|
|
743
|
+
for (const param of searchParams) {
|
|
744
|
+
const typeMapping = this.getTypeScriptTypeForSearchParameter(resourceType, param);
|
|
745
|
+
const comment = param.description ? ` /** ${param.description} */` : "";
|
|
746
|
+
interfaceFields.push(`${comment}`);
|
|
747
|
+
interfaceFields.push(` '${param.name}'?: ${typeMapping};`);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return `/**
|
|
751
|
+
* Enhanced search parameters for ${resourceType} resources
|
|
752
|
+
*/
|
|
753
|
+
export interface ${resourceType}SearchParams extends BaseEnhancedSearchParams {
|
|
754
|
+
${interfaceFields.join("\n")}
|
|
755
|
+
}`;
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Map FHIR search parameter types to TypeScript enhanced types
|
|
759
|
+
*/
|
|
760
|
+
getTypeScriptTypeForSearchParameter(resourceType, param) {
|
|
761
|
+
switch (param.type) {
|
|
762
|
+
case "string":
|
|
763
|
+
return "string | SearchModifiers['StringModifier']";
|
|
764
|
+
case "number":
|
|
765
|
+
return "number | SearchModifiers['NumberParameter']";
|
|
766
|
+
case "date":
|
|
767
|
+
return "string | SearchModifiers['DateParameter']";
|
|
768
|
+
case "token":
|
|
769
|
+
if (this.valueSetEnumsEnabled) {
|
|
770
|
+
// Look up enum type name from our preprocessing
|
|
771
|
+
const enumTypeName = this.availableEnumTypes.get(`${resourceType}${param.name}`);
|
|
772
|
+
if (enumTypeName) {
|
|
773
|
+
// Use the separate TokenSearchOptions type to avoid expanding the full union
|
|
774
|
+
// This should help TypeScript prioritize the enum values in autocomplete
|
|
775
|
+
return `${enumTypeName} | SearchModifiers['TokenSearchOptions']`;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return "string | SearchModifiers['TokenParameter']";
|
|
779
|
+
case "reference":
|
|
780
|
+
if (param.target && param.target.length > 0) {
|
|
781
|
+
// Specific target types are not encoded at type level for ReferenceParameter to keep it simple
|
|
782
|
+
return `string | SearchModifiers['ReferenceParameter']`;
|
|
783
|
+
}
|
|
784
|
+
return "string | SearchModifiers['ReferenceParameter']";
|
|
785
|
+
case "quantity":
|
|
786
|
+
return "number | string | SearchModifiers['QuantityParameter']";
|
|
787
|
+
case "uri":
|
|
788
|
+
return "string";
|
|
789
|
+
case "composite":
|
|
790
|
+
return "string";
|
|
791
|
+
default:
|
|
792
|
+
return "string";
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Get collected resource types
|
|
797
|
+
*/
|
|
798
|
+
getResourceTypes() {
|
|
799
|
+
return this.resourceTypes;
|
|
800
|
+
}
|
|
801
|
+
}
|