babelfhir-ts 1.5.19 → 1.5.21
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/out/src/cli/updateCommand.js +19 -12
- package/out/src/generator/core/constants.js +19 -0
- package/out/src/generator/core/extensionSdReader.js +99 -0
- package/out/src/generator/core/utils.js +97 -3
- package/out/src/generator/emitters/class/classTemplate.js +8 -2
- package/out/src/generator/emitters/class/sliceElementDefaults.js +5 -3
- package/out/src/generator/emitters/client/clientGenerator.js +22 -6
- package/out/src/generator/emitters/client/smartAuthGenerator.js +4 -1
- package/out/src/generator/emitters/interface/backboneSliceTyping.js +15 -4
- package/out/src/generator/emitters/interface/importManager.js +28 -2
- package/out/src/generator/emitters/interface/interfaceFieldProcessor.js +21 -16
- package/out/src/generator/emitters/interface/interfaceFieldUtils.js +16 -0
- package/out/src/generator/emitters/interface/postProcessExtensions.js +1 -0
- package/out/src/generator/emitters/validator/closedSlicingValidation.js +146 -0
- package/out/src/generator/emitters/validator/sliceDelegation.js +450 -0
- package/out/src/generator/emitters/validator/sliceExtensionValidation.js +200 -35
- package/out/src/generator/emitters/validator/sliceValidatorGenerator.js +130 -299
- package/out/src/generator/emitters/validator/sliceValidatorUtils.js +89 -16
- package/out/src/generator/emitters/validator/subExtensionScope.js +100 -0
- package/out/src/generator/emitters/validator/validatorBindingBuilder.js +113 -237
- package/out/src/generator/emitters/validator/validatorBindingLeafEmitters.js +13 -4
- package/out/src/generator/emitters/validator/validatorConstraintBuilders.js +104 -63
- package/out/src/generator/emitters/validator/validatorExpressions.js +54 -0
- package/out/src/generator/emitters/validator/validatorFieldBuilders.js +492 -46
- package/out/src/generator/emitters/validator/validatorGenerator.js +170 -169
- package/out/src/generator/emitters/validator/validatorRuntime.js +279 -0
- package/out/src/generator/emitters/validator/validatorTemplates.js +186 -54
- package/out/src/generator/emitters/zod/zodRefinementBuilder.js +29 -19
- package/out/src/generator/emitters/zod/zodSchemaGenerator.js +16 -1
- package/out/src/generator/index.js +21 -11
- package/out/src/generator/parser/packageParser.js +1 -8
- package/out/src/generator/parser/sdParser.js +1 -0
- package/out/src/generator/sdProcessor.js +303 -30
- package/out/src/generator/sdProcessorHelpers.js +1 -1
- package/out/src/generator/sdProcessorRequiredFields.js +120 -0
- package/out/src/main.js +37 -11
- package/package.json +3 -1
- package/parity-matrix.json +18 -18
|
@@ -1,4 +1,36 @@
|
|
|
1
1
|
import { buildNestedLoopCode, emitSystemPresenceLeaf, emitSystemOnlyLeaf, emitResolvedLeaf, } from './validatorBindingLeafEmitters.js';
|
|
2
|
+
/**
|
|
3
|
+
* Emit a typed read of `rel` off the resource.
|
|
4
|
+
*
|
|
5
|
+
* `rel` is a JS property path (`code`, `meta.profile`), so it goes through at()'s
|
|
6
|
+
* variadic form. The result is `unknown`, which is the point: this replaces the
|
|
7
|
+
* `resource as Record<string, any>` bag the emitted checks used to read from.
|
|
8
|
+
*/
|
|
9
|
+
function relAccessor(rel) {
|
|
10
|
+
return `at(resource, ${rel.split('.').map(segment => `'${segment}'`).join(', ')})`;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Emit `leaf` once per member of an array field, or once directly for a scalar.
|
|
14
|
+
*
|
|
15
|
+
* arr() yields nothing for absent/null/non-array values, exactly matching the
|
|
16
|
+
* `if (x && Array.isArray(x))` guard this replaces, so no scalar slips into the
|
|
17
|
+
* array branch and no array member is skipped.
|
|
18
|
+
*/
|
|
19
|
+
function emitValueCheck(output, comment, field, rel, itemVar, leaf) {
|
|
20
|
+
const accessor = relAccessor(rel);
|
|
21
|
+
if (field.isArray) {
|
|
22
|
+
output.push(`
|
|
23
|
+
// ${comment}
|
|
24
|
+
for (const ${itemVar} of arr(${accessor})) {${leaf(itemVar, ' ')}
|
|
25
|
+
}`);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
output.push(`
|
|
29
|
+
// ${comment}
|
|
30
|
+
{${leaf(accessor, ' ')}
|
|
31
|
+
}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
2
34
|
/**
|
|
3
35
|
* Build required-strength ValueSet binding validations.
|
|
4
36
|
* Validates that coded fields contain codes from their bound ValueSet.
|
|
@@ -18,17 +50,34 @@ export function buildBindingValidations(fields, fieldParentMap, arrayFieldPaths,
|
|
|
18
50
|
return false;
|
|
19
51
|
}
|
|
20
52
|
const type = field.type || field.baseTypeCode;
|
|
21
|
-
|
|
53
|
+
// FHIR permits a binding on string and uri as well as the coded types, and
|
|
54
|
+
// cdex uses one: Task.reasonCode.text is a string bound to cdex-claim-use.
|
|
55
|
+
// Excluding them meant the ValueSet was extracted and then never consulted.
|
|
56
|
+
if (!type || !['code', 'Coding', 'CodeableConcept', 'Quantity', 'string', 'uri'].includes(type))
|
|
22
57
|
return false;
|
|
23
58
|
return true;
|
|
24
59
|
})
|
|
25
|
-
.forEach(
|
|
60
|
+
.forEach(rawField => {
|
|
61
|
+
// For string and uri the element's own value is the code, which is exactly how
|
|
62
|
+
// `code` is validated, so they route through that branch.
|
|
63
|
+
const declaredType = rawField.type || rawField.baseTypeCode;
|
|
64
|
+
const field = declaredType === 'string' || declaredType === 'uri'
|
|
65
|
+
? { ...rawField, type: 'code', baseTypeCode: 'code' }
|
|
66
|
+
: rawField;
|
|
26
67
|
const relFull = field.name || '';
|
|
27
68
|
const relRaw = relFull.replace(/^[^.]+\./, '');
|
|
28
69
|
let rel = relRaw.replace(/\[x\]/g, '');
|
|
29
70
|
if (!rel)
|
|
30
71
|
return;
|
|
31
72
|
const isChoiceType = relRaw.includes('[x]');
|
|
73
|
+
// A choice element in a non-leaf position loses its concrete property name when
|
|
74
|
+
// "[x]" is stripped: Observation.value[x]:valueQuantity.comparator becomes
|
|
75
|
+
// value.comparator, and `resource.value` does not exist — us-core's BMI and
|
|
76
|
+
// BodyHeight profiles stopped compiling on exactly that. The suffix logic below
|
|
77
|
+
// only resolves a choice element in the leaf position, so anything deeper is
|
|
78
|
+
// skipped rather than emitted as an invalid accessor.
|
|
79
|
+
if (relRaw.split('.').slice(0, -1).some(segment => segment.includes('[x]')))
|
|
80
|
+
return;
|
|
32
81
|
const parentPath = fieldParentMap.get(rel);
|
|
33
82
|
const isNested = !!(parentPath && parentPath !== 'meta');
|
|
34
83
|
if (isChoiceType) {
|
|
@@ -69,77 +118,20 @@ function emitSystemPresenceFallback(output, field, rel, isNested, arrayFieldPath
|
|
|
69
118
|
}
|
|
70
119
|
}
|
|
71
120
|
function emitTopLevelSystemPresence(output, field, rel, type, vsDisplayName, vsUri) {
|
|
121
|
+
const missingSystem = (codingVar, indent) => `
|
|
122
|
+
${indent}if (${codingVar}.code !== undefined && (${codingVar}.system === undefined || ${codingVar}.system === '')) {
|
|
123
|
+
${indent} errors.push("The System URI could not be determined for the code '" + ${codingVar}.code + "' in the ValueSet '${vsDisplayName}' (${vsUri})");
|
|
124
|
+
${indent}}`;
|
|
125
|
+
const suffix = field.isArray ? ' (array' : ' (';
|
|
72
126
|
if (type === 'CodeableConcept') {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (_bRes.${rel} && Array.isArray(_bRes.${rel})) {
|
|
77
|
-
for (const _spItem of _bRes.${rel}) {
|
|
78
|
-
const _spCoding = codings(_spItem);
|
|
79
|
-
if (_spCoding) { for (const _c of _spCoding) { if (_c.code !== undefined && (_c.system === undefined || _c.system === '')) {
|
|
80
|
-
errors.push("The System URI could not be determined for the code '" + _c.code + "' in the ValueSet '${vsDisplayName}' (${vsUri})");
|
|
81
|
-
} } }
|
|
82
|
-
}
|
|
83
|
-
}`);
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
output.push(`
|
|
87
|
-
// Required binding system-presence fallback for ${rel} (ValueSet unresolved)
|
|
88
|
-
{
|
|
89
|
-
const _spCoding = codings(_bRes.${rel});
|
|
90
|
-
if (_spCoding) { for (const _c of _spCoding) { if (_c.code !== undefined && (_c.system === undefined || _c.system === '')) {
|
|
91
|
-
errors.push("The System URI could not be determined for the code '" + _c.code + "' in the ValueSet '${vsDisplayName}' (${vsUri})");
|
|
92
|
-
} } }
|
|
93
|
-
}`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
else if (type === 'Coding') {
|
|
97
|
-
if (field.isArray) {
|
|
98
|
-
output.push(`
|
|
99
|
-
// Required binding system-presence fallback for ${rel} (Coding array, ValueSet unresolved)
|
|
100
|
-
if (_bRes.${rel} && Array.isArray(_bRes.${rel})) {
|
|
101
|
-
for (const _spC of _bRes.${rel}) {
|
|
102
|
-
const _c = _spC as { code?: string; system?: string };
|
|
103
|
-
if (_c.code !== undefined && (_c.system === undefined || _c.system === '')) {
|
|
104
|
-
errors.push("The System URI could not be determined for the code '" + _c.code + "' in the ValueSet '${vsDisplayName}' (${vsUri})");
|
|
105
|
-
}
|
|
127
|
+
emitValueCheck(output, `Required binding system-presence fallback for ${rel}${suffix}ValueSet unresolved)`, field, rel, '_spItem', (value, indent) => `
|
|
128
|
+
${indent}for (const _c of codings(${value})) {${missingSystem('_c', indent + ' ')}
|
|
129
|
+
${indent}}`);
|
|
106
130
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
// Required binding system-presence fallback for ${rel} (Coding, ValueSet unresolved)
|
|
112
|
-
if (_bRes.${rel}) {
|
|
113
|
-
const _c = _bRes.${rel} as { code?: string; system?: string };
|
|
114
|
-
if (_c.code !== undefined && (_c.system === undefined || _c.system === '')) {
|
|
115
|
-
errors.push("The System URI could not be determined for the code '" + _c.code + "' in the ValueSet '${vsDisplayName}' (${vsUri})");
|
|
116
|
-
}
|
|
117
|
-
}`);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
else if (type === 'Quantity') {
|
|
121
|
-
if (field.isArray) {
|
|
122
|
-
output.push(`
|
|
123
|
-
// Required binding system-presence fallback for ${rel} (Quantity array, ValueSet unresolved)
|
|
124
|
-
if (_bRes.${rel} && Array.isArray(_bRes.${rel})) {
|
|
125
|
-
for (const _spQ of _bRes.${rel}) {
|
|
126
|
-
const _q = _spQ as { code?: string; system?: string };
|
|
127
|
-
if (_q.code !== undefined && (_q.system === undefined || _q.system === '')) {
|
|
128
|
-
errors.push("The System URI could not be determined for the code '" + _q.code + "' in the ValueSet '${vsDisplayName}' (${vsUri})");
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}`);
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
output.push(`
|
|
135
|
-
// Required binding system-presence fallback for ${rel} (Quantity, ValueSet unresolved)
|
|
136
|
-
if (_bRes.${rel}) {
|
|
137
|
-
const _q = _bRes.${rel} as { code?: string; system?: string };
|
|
138
|
-
if (_q.code !== undefined && (_q.system === undefined || _q.system === '')) {
|
|
139
|
-
errors.push("The System URI could not be determined for the code '" + _q.code + "' in the ValueSet '${vsDisplayName}' (${vsUri})");
|
|
140
|
-
}
|
|
141
|
-
}`);
|
|
142
|
-
}
|
|
131
|
+
else {
|
|
132
|
+
// Coding and Quantity both carry code/system directly and share this message.
|
|
133
|
+
emitValueCheck(output, `Required binding system-presence fallback for ${rel} (${type}${field.isArray ? ' array' : ''}, ValueSet unresolved)`, field, rel, '_spC', (value, indent) => `
|
|
134
|
+
${indent}const _c = coding(${value});${missingSystem('_c', indent)}`);
|
|
143
135
|
}
|
|
144
136
|
}
|
|
145
137
|
// ── System-only binding ─────────────────────────────────────────────────────
|
|
@@ -160,89 +152,33 @@ function emitSystemOnlyBinding(output, field, rel, isNested, vsInfo, arrayFieldP
|
|
|
160
152
|
}
|
|
161
153
|
}
|
|
162
154
|
function emitTopLevelSystemOnly(output, field, rel, type, vsDisplayName, sysVsUri, systemsLiteral) {
|
|
155
|
+
const arraySuffix = field.isArray ? ' (array)' : '';
|
|
163
156
|
if (type === 'CodeableConcept') {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const _codes = _scc.coding.filter((_c: { code?: string; system?: string }) => _c.code !== undefined).map((_c: { code?: string; system?: string }) => (_c.system || "") + "#" + _c.code).join(", ");
|
|
174
|
-
errors.push("None of the codings provided are in the value set '${vsDisplayName}' (${sysVsUri}), and a coding from this value set is required) (codes = " + _codes + ")");
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}`);
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
output.push(`
|
|
182
|
-
// Required ValueSet system-level binding validation for ${rel}
|
|
183
|
-
if (_bRes.${rel}) {
|
|
184
|
-
const _sccCoding = (_bRes.${rel} as { coding?: Array<{ code?: string; system?: string }> }).coding;
|
|
185
|
-
if (_sccCoding && _sccCoding.some((_c: { code?: string }) => _c.code !== undefined)) {
|
|
186
|
-
const _allowedSystems = ${systemsLiteral};
|
|
187
|
-
if (!_sccCoding.some((_c: { system?: string }) => _c.system !== undefined && _allowedSystems.includes(_c.system))) {
|
|
188
|
-
const _codes = _sccCoding.filter((_c: { code?: string; system?: string }) => _c.code !== undefined).map((_c: { code?: string; system?: string }) => (_c.system || "") + "#" + _c.code).join(", ");
|
|
189
|
-
errors.push("None of the codings provided are in the value set '${vsDisplayName}' (${sysVsUri}), and a coding from this value set is required) (codes = " + _codes + ")");
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}`);
|
|
193
|
-
}
|
|
157
|
+
emitValueCheck(output, `Required ValueSet system-level binding validation for ${rel}${arraySuffix}`, field, rel, '_sysItem', (value, indent) => `
|
|
158
|
+
${indent}const _sccCoding = codings(${value});
|
|
159
|
+
${indent}if (_sccCoding.some((_c) => _c.code !== undefined)) {
|
|
160
|
+
${indent} const _allowedSystems = ${systemsLiteral};
|
|
161
|
+
${indent} if (!_sccCoding.some((_c) => _c.system !== undefined && _allowedSystems.includes(_c.system))) {
|
|
162
|
+
${indent} const _codes = _sccCoding.filter((_c) => _c.code !== undefined).map((_c) => (_c.system || "") + "#" + _c.code).join(", ");
|
|
163
|
+
${indent} errors.push("None of the codings provided are in the value set '${vsDisplayName}' (${sysVsUri}), and a coding from this value set is required) (codes = " + _codes + ")");
|
|
164
|
+
${indent} }
|
|
165
|
+
${indent}}`);
|
|
194
166
|
}
|
|
195
167
|
else if (type === 'Coding') {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const _allowedSystems = ${systemsLiteral};
|
|
203
|
-
if (_sc.code !== undefined && _sc.system !== undefined && !_allowedSystems.includes(_sc.system)) {
|
|
204
|
-
errors.push("Code '" + _sc.code + "' does not exist in the value set '${vsDisplayName}' (${sysVsUri}), but the binding is of strength 'required'");
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}`);
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
output.push(`
|
|
211
|
-
// Required ValueSet system-level binding validation for ${rel}
|
|
212
|
-
if (_bRes.${rel} && (_bRes.${rel} as { code?: string; system?: string }).code !== undefined) {
|
|
213
|
-
const _allowedSystems = ${systemsLiteral};
|
|
214
|
-
const _sc = _bRes.${rel} as { code?: string; system?: string };
|
|
215
|
-
if (_sc.system !== undefined && !_allowedSystems.includes(_sc.system)) {
|
|
216
|
-
errors.push("Code '" + _sc.code + "' does not exist in the value set '${vsDisplayName}' (${sysVsUri}), but the binding is of strength 'required'");
|
|
217
|
-
}
|
|
218
|
-
}`);
|
|
219
|
-
}
|
|
168
|
+
emitValueCheck(output, `Required ValueSet system-level binding validation for ${rel}${arraySuffix}`, field, rel, '_sCoding', (value, indent) => `
|
|
169
|
+
${indent}const _sc = coding(${value});
|
|
170
|
+
${indent}const _allowedSystems = ${systemsLiteral};
|
|
171
|
+
${indent}if (_sc.code !== undefined && _sc.system !== undefined && !_allowedSystems.includes(_sc.system)) {
|
|
172
|
+
${indent} errors.push("Code '" + _sc.code + "' does not exist in the value set '${vsDisplayName}' (${sysVsUri}), but the binding is of strength 'required'");
|
|
173
|
+
${indent}}`);
|
|
220
174
|
}
|
|
221
175
|
else if (type === 'Quantity') {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const _allowedSystems = ${systemsLiteral};
|
|
229
|
-
if (_q.code !== undefined && _q.system !== undefined && !_allowedSystems.includes(_q.system)) {
|
|
230
|
-
errors.push("Code '" + _q.code + "' from system '" + _q.system + "' does not exist in the value set '${vsDisplayName}' (${sysVsUri}), but the binding is of strength 'required'");
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}`);
|
|
234
|
-
}
|
|
235
|
-
else {
|
|
236
|
-
output.push(`
|
|
237
|
-
// Required ValueSet system-level binding validation for ${rel} (Quantity)
|
|
238
|
-
if (_bRes.${rel}) {
|
|
239
|
-
const _q = _bRes.${rel} as { code?: string; system?: string };
|
|
240
|
-
const _allowedSystems = ${systemsLiteral};
|
|
241
|
-
if (_q.code !== undefined && _q.system !== undefined && !_allowedSystems.includes(_q.system)) {
|
|
242
|
-
errors.push("Code '" + _q.code + "' from system '" + _q.system + "' does not exist in the value set '${vsDisplayName}' (${sysVsUri}), but the binding is of strength 'required'");
|
|
243
|
-
}
|
|
244
|
-
}`);
|
|
245
|
-
}
|
|
176
|
+
emitValueCheck(output, `Required ValueSet system-level binding validation for ${rel} (Quantity${field.isArray ? ' array' : ''})`, field, rel, '_sysQ', (value, indent) => `
|
|
177
|
+
${indent}const _q = coding(${value});
|
|
178
|
+
${indent}const _allowedSystems = ${systemsLiteral};
|
|
179
|
+
${indent}if (_q.code !== undefined && _q.system !== undefined && !_allowedSystems.includes(_q.system)) {
|
|
180
|
+
${indent} errors.push("Code '" + _q.code + "' from system '" + _q.system + "' does not exist in the value set '${vsDisplayName}' (${sysVsUri}), but the binding is of strength 'required'");
|
|
181
|
+
${indent}}`);
|
|
246
182
|
}
|
|
247
183
|
}
|
|
248
184
|
// ── Resolved code-level binding ─────────────────────────────────────────────
|
|
@@ -261,100 +197,40 @@ function emitResolvedBinding(output, field, rel, isNested, vsInfo, arrayFieldPat
|
|
|
261
197
|
}
|
|
262
198
|
}
|
|
263
199
|
function emitTopLevelResolved(output, field, rel, type, vsName, vsDisplayName, vsUri) {
|
|
200
|
+
const arraySuffix = field.isArray ? ' (array)' : '';
|
|
201
|
+
const notInValueSet = (codeExpr) => `"Code '" + ${codeExpr} + "' does not exist in the value set '${vsDisplayName}' (${vsUri}), but the binding is of strength 'required'"`;
|
|
264
202
|
if (type === 'code') {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
274
|
-
}`);
|
|
275
|
-
}
|
|
276
|
-
else {
|
|
277
|
-
output.push(`
|
|
278
|
-
// Required ValueSet binding validation for ${rel}
|
|
279
|
-
if (_bRes.${rel} !== undefined && !isValid${vsName}Code(_bRes.${rel} as string)) {
|
|
280
|
-
errors.push("Code '" + _bRes.${rel} + "' does not exist in the value set '${vsDisplayName}' (${vsUri}), but the binding is of strength 'required'");
|
|
281
|
-
}`);
|
|
282
|
-
}
|
|
203
|
+
// A `code` element is a bare string. Non-strings are malformed JSON and are
|
|
204
|
+
// reported by the structural type checks, so they are not run through the
|
|
205
|
+
// ValueSet lookup here.
|
|
206
|
+
emitValueCheck(output, `Required ValueSet binding validation for ${rel}${arraySuffix}`, field, rel, '_code', (value, indent) => `
|
|
207
|
+
${indent}const _codeValue = ${value};
|
|
208
|
+
${indent}if (typeof _codeValue === 'string' && !isValid${vsName}Code(_codeValue)) {
|
|
209
|
+
${indent} errors.push(${notInValueSet('_codeValue')});
|
|
210
|
+
${indent}}`);
|
|
283
211
|
}
|
|
284
212
|
else if (type === 'Coding') {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
const _c = _coding as { code?: string };
|
|
291
|
-
if (_c.code !== undefined && !isValid${vsName}Code(_c.code)) {
|
|
292
|
-
errors.push("Code '" + _c.code + "' does not exist in the value set '${vsDisplayName}' (${vsUri}), but the binding is of strength 'required'");
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}`);
|
|
296
|
-
}
|
|
297
|
-
else {
|
|
298
|
-
output.push(`
|
|
299
|
-
// Required ValueSet binding validation for ${rel}
|
|
300
|
-
if (_bRes.${rel} && (_bRes.${rel} as { code?: string }).code !== undefined) {
|
|
301
|
-
if (!isValid${vsName}Code((_bRes.${rel} as { code?: string }).code as string)) {
|
|
302
|
-
errors.push("Code '" + (_bRes.${rel} as { code?: string }).code + "' does not exist in the value set '${vsDisplayName}' (${vsUri}), but the binding is of strength 'required'");
|
|
303
|
-
}
|
|
304
|
-
}`);
|
|
305
|
-
}
|
|
213
|
+
emitValueCheck(output, `Required ValueSet binding validation for ${rel}${arraySuffix}`, field, rel, '_coding', (value, indent) => `
|
|
214
|
+
${indent}const _c = coding(${value});
|
|
215
|
+
${indent}if (_c.code !== undefined && !isValid${vsName}Code(_c.code)) {
|
|
216
|
+
${indent} errors.push(${notInValueSet('_c.code')});
|
|
217
|
+
${indent}}`);
|
|
306
218
|
}
|
|
307
219
|
else if (type === 'CodeableConcept') {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
if (
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const _codes = _cc.coding.filter((_c: { code?: string; system?: string }) => _c.code !== undefined).map((_c: { code?: string; system?: string }) => (_c.system || "") + "#" + _c.code).join(", ");
|
|
317
|
-
errors.push("None of the codings provided are in the value set '${vsDisplayName}' (${vsUri}), and a coding from this value set is required) (codes = " + _codes + ")");
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}`);
|
|
322
|
-
}
|
|
323
|
-
else {
|
|
324
|
-
output.push(`
|
|
325
|
-
// Required ValueSet binding validation for ${rel}
|
|
326
|
-
if (_bRes.${rel}) {
|
|
327
|
-
const _ccCoding = (_bRes.${rel} as { coding?: Array<{ code?: string; system?: string }> }).coding;
|
|
328
|
-
if (_ccCoding && _ccCoding.some((_c: { code?: string }) => _c.code !== undefined)) {
|
|
329
|
-
if (!_ccCoding.some((_c: { code?: string }) => _c.code !== undefined && isValid${vsName}Code(_c.code))) {
|
|
330
|
-
const _codes = _ccCoding.filter((_c: { code?: string; system?: string }) => _c.code !== undefined).map((_c: { code?: string; system?: string }) => (_c.system || "") + "#" + _c.code).join(", ");
|
|
331
|
-
errors.push("None of the codings provided are in the value set '${vsDisplayName}' (${vsUri}), and a coding from this value set is required) (codes = " + _codes + ")");
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}`);
|
|
335
|
-
}
|
|
220
|
+
emitValueCheck(output, `Required ValueSet binding validation for ${rel}${arraySuffix}`, field, rel, '_item', (value, indent) => `
|
|
221
|
+
${indent}const _ccCoding = codings(${value});
|
|
222
|
+
${indent}if (_ccCoding.some((_c) => _c.code !== undefined)) {
|
|
223
|
+
${indent} if (!_ccCoding.some((_c) => _c.code !== undefined && isValid${vsName}Code(_c.code))) {
|
|
224
|
+
${indent} const _codes = _ccCoding.filter((_c) => _c.code !== undefined).map((_c) => (_c.system || "") + "#" + _c.code).join(", ");
|
|
225
|
+
${indent} errors.push("None of the codings provided are in the value set '${vsDisplayName}' (${vsUri}), and a coding from this value set is required) (codes = " + _codes + ")");
|
|
226
|
+
${indent} }
|
|
227
|
+
${indent}}`);
|
|
336
228
|
}
|
|
337
229
|
else if (type === 'Quantity') {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const _q = _item as { code?: string; system?: string };
|
|
344
|
-
if (_q.code !== undefined && !isValid${vsName}Code(_q.code)) {
|
|
345
|
-
errors.push("Code '" + _q.code + "' from system '" + (_q.system || "") + "' does not exist in the value set '${vsDisplayName}' (${vsUri}), but the binding is of strength 'required'");
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
}`);
|
|
349
|
-
}
|
|
350
|
-
else {
|
|
351
|
-
output.push(`
|
|
352
|
-
// Required ValueSet binding validation for ${rel} (Quantity.code)
|
|
353
|
-
if (_bRes.${rel} && (_bRes.${rel} as { code?: string }).code !== undefined) {
|
|
354
|
-
if (!isValid${vsName}Code((_bRes.${rel} as { code?: string }).code as string)) {
|
|
355
|
-
errors.push("Code '" + (_bRes.${rel} as { code?: string }).code + "' from system '" + ((_bRes.${rel} as { system?: string }).system || "") + "' does not exist in the value set '${vsDisplayName}' (${vsUri}), but the binding is of strength 'required'");
|
|
356
|
-
}
|
|
357
|
-
}`);
|
|
358
|
-
}
|
|
230
|
+
emitValueCheck(output, `Required ValueSet binding validation for ${rel} (Quantity.code${field.isArray ? ', array' : ''})`, field, rel, '_item', (value, indent) => `
|
|
231
|
+
${indent}const _q = coding(${value});
|
|
232
|
+
${indent}if (_q.code !== undefined && !isValid${vsName}Code(_q.code)) {
|
|
233
|
+
${indent} errors.push("Code '" + _q.code + "' from system '" + (_q.system || "") + "' does not exist in the value set '${vsDisplayName}' (${vsUri}), but the binding is of strength 'required'");
|
|
234
|
+
${indent}}`);
|
|
359
235
|
}
|
|
360
236
|
}
|
|
@@ -11,16 +11,25 @@ export function buildNestedLoopCode(field, rel, arrayFieldPaths, emitLeaf, comme
|
|
|
11
11
|
const isArr = arrayFieldPaths.has(partialPath) || arrayFieldPaths.has(partialPath.split(':')[0]);
|
|
12
12
|
loopInfos.push({ segment: parts[i], isArray: isArr });
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
// A nested path with no array ancestor is still reachable: the loop below already
|
|
15
|
+
// descends plain levels with an `if (… !== undefined)`. Requiring an array meant
|
|
16
|
+
// a binding like Task.reasonCode.text (reasonCode is 0..1) was never checked, even
|
|
17
|
+
// though its ValueSet had been extracted — the import was emitted and nothing used
|
|
18
|
+
// it.
|
|
19
|
+
if (loopInfos.length === 0)
|
|
15
20
|
return undefined;
|
|
16
21
|
let code = `\n // ${comment}`;
|
|
17
22
|
let indent = ' ';
|
|
18
23
|
let currentVar = 'resource';
|
|
19
24
|
for (let i = 0; i < loopInfos.length; i++) {
|
|
20
25
|
const li = loopInfos[i];
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
|
|
26
|
+
// Every level goes through `at()`, including the first. Reading `resource.x`
|
|
27
|
+
// directly assumed the emitted interface declares `x`, which a logical model
|
|
28
|
+
// does not: SDCQuestionLibrary derives from Resource, so `resource.address`
|
|
29
|
+
// failed to compile and took the whole package's generation down with it.
|
|
30
|
+
// `at()` yields `unknown`, which is all the `!== undefined` guard and `arr()`
|
|
31
|
+
// need, so this stays as checked as the deeper levels already were.
|
|
32
|
+
const accessor = `at(${currentVar}, '${li.segment}')`;
|
|
24
33
|
if (li.isArray) {
|
|
25
34
|
const loopVar = `_nb${i}`;
|
|
26
35
|
// `arr()` is the Array.isArray guard: non-arrays yield no iterations.
|