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.
Files changed (38) hide show
  1. package/out/src/cli/updateCommand.js +19 -12
  2. package/out/src/generator/core/constants.js +19 -0
  3. package/out/src/generator/core/extensionSdReader.js +99 -0
  4. package/out/src/generator/core/utils.js +97 -3
  5. package/out/src/generator/emitters/class/classTemplate.js +8 -2
  6. package/out/src/generator/emitters/class/sliceElementDefaults.js +5 -3
  7. package/out/src/generator/emitters/client/clientGenerator.js +22 -6
  8. package/out/src/generator/emitters/client/smartAuthGenerator.js +4 -1
  9. package/out/src/generator/emitters/interface/backboneSliceTyping.js +15 -4
  10. package/out/src/generator/emitters/interface/importManager.js +28 -2
  11. package/out/src/generator/emitters/interface/interfaceFieldProcessor.js +21 -16
  12. package/out/src/generator/emitters/interface/interfaceFieldUtils.js +16 -0
  13. package/out/src/generator/emitters/interface/postProcessExtensions.js +1 -0
  14. package/out/src/generator/emitters/validator/closedSlicingValidation.js +146 -0
  15. package/out/src/generator/emitters/validator/sliceDelegation.js +450 -0
  16. package/out/src/generator/emitters/validator/sliceExtensionValidation.js +200 -35
  17. package/out/src/generator/emitters/validator/sliceValidatorGenerator.js +130 -299
  18. package/out/src/generator/emitters/validator/sliceValidatorUtils.js +89 -16
  19. package/out/src/generator/emitters/validator/subExtensionScope.js +100 -0
  20. package/out/src/generator/emitters/validator/validatorBindingBuilder.js +113 -237
  21. package/out/src/generator/emitters/validator/validatorBindingLeafEmitters.js +13 -4
  22. package/out/src/generator/emitters/validator/validatorConstraintBuilders.js +104 -63
  23. package/out/src/generator/emitters/validator/validatorExpressions.js +54 -0
  24. package/out/src/generator/emitters/validator/validatorFieldBuilders.js +492 -46
  25. package/out/src/generator/emitters/validator/validatorGenerator.js +170 -169
  26. package/out/src/generator/emitters/validator/validatorRuntime.js +279 -0
  27. package/out/src/generator/emitters/validator/validatorTemplates.js +186 -54
  28. package/out/src/generator/emitters/zod/zodRefinementBuilder.js +29 -19
  29. package/out/src/generator/emitters/zod/zodSchemaGenerator.js +16 -1
  30. package/out/src/generator/index.js +21 -11
  31. package/out/src/generator/parser/packageParser.js +1 -8
  32. package/out/src/generator/parser/sdParser.js +1 -0
  33. package/out/src/generator/sdProcessor.js +303 -30
  34. package/out/src/generator/sdProcessorHelpers.js +1 -1
  35. package/out/src/generator/sdProcessorRequiredFields.js +120 -0
  36. package/out/src/main.js +37 -11
  37. package/package.json +3 -1
  38. 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
- if (!type || !['code', 'Coding', 'CodeableConcept', 'Quantity'].includes(type))
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(field => {
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
- if (field.isArray) {
74
- output.push(`
75
- // Required binding system-presence fallback for ${rel} (array, ValueSet unresolved)
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
- else {
110
- output.push(`
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
- if (field.isArray) {
165
- output.push(`
166
- // Required ValueSet system-level binding validation for ${rel} (array)
167
- if (_bRes.${rel} && Array.isArray(_bRes.${rel})) {
168
- for (const _sysItem of _bRes.${rel}) {
169
- const _scc = _sysItem as { coding?: Array<{ code?: string; system?: string }> };
170
- if (_scc.coding && _scc.coding.some((_c: { code?: string }) => _c.code !== undefined)) {
171
- const _allowedSystems = ${systemsLiteral};
172
- if (!_scc.coding.some((_c: { system?: string }) => _c.system !== undefined && _allowedSystems.includes(_c.system))) {
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
- if (field.isArray) {
197
- output.push(`
198
- // Required ValueSet system-level binding validation for ${rel} (array)
199
- if (_bRes.${rel} && Array.isArray(_bRes.${rel})) {
200
- for (const _sCoding of _bRes.${rel}) {
201
- const _sc = _sCoding as { code?: string; system?: string };
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
- if (field.isArray) {
223
- output.push(`
224
- // Required ValueSet system-level binding validation for ${rel} (Quantity array)
225
- if (_bRes.${rel} && Array.isArray(_bRes.${rel})) {
226
- for (const _sysQ of _bRes.${rel}) {
227
- const _q = _sysQ as { code?: string; system?: string };
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
- if (field.isArray) {
266
- output.push(`
267
- // Required ValueSet binding validation for ${rel} (array)
268
- if (_bRes.${rel} && Array.isArray(_bRes.${rel})) {
269
- for (const _code of _bRes.${rel}) {
270
- if (_code !== undefined && !isValid${vsName}Code(_code as string)) {
271
- errors.push("Code '" + _code + "' does not exist in the value set '${vsDisplayName}' (${vsUri}), but the binding is of strength 'required'");
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
- if (field.isArray) {
286
- output.push(`
287
- // Required ValueSet binding validation for ${rel} (array)
288
- if (_bRes.${rel} && Array.isArray(_bRes.${rel})) {
289
- for (const _coding of _bRes.${rel}) {
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
- if (field.isArray) {
309
- output.push(`
310
- // Required ValueSet binding validation for ${rel} (array)
311
- if (_bRes.${rel} && Array.isArray(_bRes.${rel})) {
312
- for (const _item of _bRes.${rel}) {
313
- const _cc = _item as { coding?: Array<{ code?: string; system?: string }> };
314
- if (_cc.coding && _cc.coding.some((_c: { code?: string }) => _c.code !== undefined)) {
315
- if (!_cc.coding.some((_c: { code?: string }) => _c.code !== undefined && isValid${vsName}Code(_c.code))) {
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
- if (field.isArray) {
339
- output.push(`
340
- // Required ValueSet binding validation for ${rel} (Quantity.code, array)
341
- if (_bRes.${rel} && Array.isArray(_bRes.${rel})) {
342
- for (const _item of _bRes.${rel}) {
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
- if (!loopInfos.some(li => li.isArray))
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
- // Level 0 reads off the typed `resource`; deeper levels hold `unknown`, so
22
- // they go through `at()` instead of being cast to `any`.
23
- const accessor = i === 0 ? `resource.${li.segment}` : `at(${currentVar}, '${li.segment}')`;
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.