fhirsmith 0.11.1 → 0.12.0
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/CHANGELOG.md +85 -0
- package/config-template.json +18 -1
- package/library/folder-content-loader.js +111 -21
- package/package.json +3 -3
- package/publisher/publisher.js +153 -5
- package/publisher/validation.js +198 -0
- package/server.js +12 -12
- package/stats-db.js +127 -0
- package/stats.js +172 -6
- package/translations/Messages.properties +4 -1
- package/tx/cs/cs-api.js +9 -3
- package/tx/cs/cs-api.md +1 -1
- package/tx/cs/cs-snomed.js +131 -65
- package/tx/library/conceptmap.js +39 -0
- package/tx/library/designations.js +9 -2
- package/tx/library/parameter-check.js +139 -0
- package/tx/library.js +62 -9
- package/tx/operation-context.js +220 -10
- package/tx/params.js +226 -175
- package/tx/sct/expressions.js +485 -0
- package/tx/tx.fhir.org.yml +1 -1
- package/tx/tx.js +57 -50
- package/tx/workers/batch-validate.js +5 -2
- package/tx/workers/cache-control.js +82 -6
- package/tx/workers/expand.js +80 -23
- package/tx/workers/lookup.js +14 -1
- package/tx/workers/translate.js +461 -30
- package/tx/workers/validate.js +7 -1
- package/tx/workers/worker.js +22 -10
- package/tx/xml/xml-base.js +21 -1
- package/tx/xversion/xv-conceptmap.js +81 -0
- package/tx/xversion/xv-parameters.js +8 -0
- package/utilities/merge-audit.py +178 -0
- package/utilities/why-merge-audit.md +39 -0
- package/tx/data/snomed-subset.txt +0 -84
package/tx/params.js
CHANGED
|
@@ -61,6 +61,11 @@ class TxParameters {
|
|
|
61
61
|
this.FValueSetVersionRules = null;
|
|
62
62
|
this.FUid = '';
|
|
63
63
|
|
|
64
|
+
// names of single-valued parameters that have been set explicitly, so that
|
|
65
|
+
// parameters seen later with overwrite=false (i.e. from a ValueSet's
|
|
66
|
+
// embedded expansion parameters) don't clobber them. See seeParameter.
|
|
67
|
+
this.FSeen = new Set;
|
|
68
|
+
|
|
64
69
|
this.FActiveOnly = false;
|
|
65
70
|
this.FExcludeNested = false;
|
|
66
71
|
this.FExcludeNotForUI = false;
|
|
@@ -104,173 +109,9 @@ class TxParameters {
|
|
|
104
109
|
}
|
|
105
110
|
|
|
106
111
|
for (let p of params.parameter) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
this.seeVersionRule(getValuePrimitive(p), false,'default');
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
case 'check-system-version': {
|
|
114
|
-
this.seeVersionRule(getValuePrimitive(p), false, 'check');
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
117
|
-
case 'force-system-version': {
|
|
118
|
-
this.seeVersionRule(getValuePrimitive(p), false, 'override');
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
case 'default-valueset-version': {
|
|
122
|
-
this.seeVersionRule(getValuePrimitive(p), true, 'default');
|
|
123
|
-
break;
|
|
124
|
-
}
|
|
125
|
-
case 'force-valueset-version': {
|
|
126
|
-
this.seeVersionRule(getValuePrimitive(p), true, 'override');
|
|
127
|
-
break;
|
|
128
|
-
}
|
|
129
|
-
case 'check-valueset-version': {
|
|
130
|
-
this.seeVersionRule(getValuePrimitive(p), true, 'check');
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
case 'displayLanguage': {
|
|
135
|
-
try {
|
|
136
|
-
const lang = getValuePrimitive(p);
|
|
137
|
-
this.DisplayLanguages = Languages.fromAcceptLanguage(lang, this.languageDefinitions, !this.validating);
|
|
138
|
-
if (lang) this.FHasDisplayLanguages = true;
|
|
139
|
-
} catch (error) {
|
|
140
|
-
throw new Issue("error", "processing", null, 'INVALID_DISPLAY_NAME', this.i18n.translate('INVALID_DISPLAY_NAME', this.HTTPLanguages, [getValuePrimitive(p)]), "invalid-display").handleAsOO(400);
|
|
141
|
-
}
|
|
142
|
-
break;
|
|
143
|
-
}
|
|
144
|
-
case 'designation': {
|
|
145
|
-
this.designations.push(getValuePrimitive(p));
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
case 'property': {
|
|
149
|
-
this.properties.push(getValuePrimitive(p));
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
case 'no-cache': {
|
|
153
|
-
// Write FUid (the field the cache key reads via hashSource); writing
|
|
154
|
-
// `this.uid` was a no-op so no-cache=true never busted the cache.
|
|
155
|
-
// Accept both the string ('true') and boolean (valueBoolean) forms.
|
|
156
|
-
if (strToBool(getValuePrimitive(p), false)) this.FUid = crypto.randomUUID();
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
159
|
-
case '_incomplete':
|
|
160
|
-
case 'limitedExpansion': {
|
|
161
|
-
let value = getValuePrimitive(p);
|
|
162
|
-
this.limitedExpansion = strToBool(value, false);
|
|
163
|
-
break;
|
|
164
|
-
}
|
|
165
|
-
case 'includeDesignations': {
|
|
166
|
-
let value = getValuePrimitive(p);
|
|
167
|
-
this.includeDesignations = strToBool(value, false);
|
|
168
|
-
break;
|
|
169
|
-
}
|
|
170
|
-
case 'includeDefinition': {
|
|
171
|
-
let value = getValuePrimitive(p);
|
|
172
|
-
this.includeDefinition = strToBool(value, false);
|
|
173
|
-
break;
|
|
174
|
-
}
|
|
175
|
-
case 'activeOnly': {
|
|
176
|
-
let value = getValuePrimitive(p);
|
|
177
|
-
this.activeOnly = strToBool(value, false);
|
|
178
|
-
break;
|
|
179
|
-
}
|
|
180
|
-
case 'excludeNested': {
|
|
181
|
-
let value = getValuePrimitive(p);
|
|
182
|
-
this.excludeNested = strToBool(value, false);
|
|
183
|
-
break;
|
|
184
|
-
}
|
|
185
|
-
case 'excludeNotForUI': {
|
|
186
|
-
let value = getValuePrimitive(p);
|
|
187
|
-
this.excludeNotForUI = strToBool(value, false);
|
|
188
|
-
break;
|
|
189
|
-
}
|
|
190
|
-
case 'excludePostCoordinated': {
|
|
191
|
-
let value = getValuePrimitive(p);
|
|
192
|
-
this.excludePostCoordinated = strToBool(value, false);
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
case 'default-to-latest-version': {
|
|
196
|
-
let value = getValuePrimitive(p);
|
|
197
|
-
this.defaultToLatestVersion = strToBool(value, false);
|
|
198
|
-
break;
|
|
199
|
-
}
|
|
200
|
-
case 'incomplete-ok': {
|
|
201
|
-
let value = getValuePrimitive(p);
|
|
202
|
-
this.incompleteOK = strToBool(value, false);
|
|
203
|
-
break;
|
|
204
|
-
}
|
|
205
|
-
case 'diagnostics': {
|
|
206
|
-
let value = getValuePrimitive(p);
|
|
207
|
-
this.diagnostics = strToBool(value, false);
|
|
208
|
-
break;
|
|
209
|
-
}
|
|
210
|
-
case 'lenient-display-validation': {
|
|
211
|
-
if (getValuePrimitive(p) == true) this.displayWarning = true;
|
|
212
|
-
break;
|
|
213
|
-
}
|
|
214
|
-
case 'valueset-membership-only': {
|
|
215
|
-
if (getValuePrimitive(p) == true) this.membershipOnly = true;
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
case 'versionsMatch' : {
|
|
219
|
-
if (getValuePrimitive(p) == true) this.FVersionsMatch = true;
|
|
220
|
-
break;
|
|
221
|
-
}
|
|
222
|
-
case 'profile' : {
|
|
223
|
-
let value = p.resource;
|
|
224
|
-
if (value && (value.resourceType === 'Parameters' || value.resourceType === 'ExpansionProfile')) {
|
|
225
|
-
this.readParams(value);
|
|
226
|
-
}
|
|
227
|
-
break;
|
|
228
|
-
}
|
|
229
|
-
// eslint-disable-next-line no-fallthrough
|
|
230
|
-
case 'term': // jQuery support
|
|
231
|
-
case 'filter' : {
|
|
232
|
-
this.filter = getValuePrimitive(p);
|
|
233
|
-
break;
|
|
234
|
-
}
|
|
235
|
-
case 'count' : {
|
|
236
|
-
this.count = Utilities.parseIntOrDefault(getValuePrimitive(p), -1);
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
|
-
case 'offset' : {
|
|
240
|
-
this.offset = Utilities.parseIntOrDefault(getValuePrimitive(p), -1);
|
|
241
|
-
break;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
case 'limit' : {
|
|
245
|
-
this.limit = Utilities.parseIntOrDefault(getValuePrimitive(p), -1);
|
|
246
|
-
break;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
case 'useSupplement' : {
|
|
250
|
-
this.supplements.add(getValuePrimitive(p));
|
|
251
|
-
break;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
case 'abstract': {
|
|
255
|
-
if (getValuePrimitive(p) == true) {
|
|
256
|
-
this.abstractOk = true;
|
|
257
|
-
} else if (getValuePrimitive(p) == false) {
|
|
258
|
-
this.abstractOk = false;
|
|
259
|
-
}
|
|
260
|
-
break;
|
|
261
|
-
}
|
|
262
|
-
case 'inferSystem': {
|
|
263
|
-
if (getValuePrimitive(p) == true) this.inferSystem = true;
|
|
264
|
-
break;
|
|
265
|
-
}
|
|
266
|
-
case 'sort': {
|
|
267
|
-
this.sort = getValuePrimitive(p);
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
case "exclude-system": {
|
|
271
|
-
throw new Issue('error', 'not-supported', null, null, "The parameter 'exclude-system' is not supported by this system", null, 400);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
112
|
+
// parameters that come from the request itself always win over anything
|
|
113
|
+
// seen earlier (e.g. from a ValueSet's embedded expansion parameters)
|
|
114
|
+
this.seeParameter(p.name, p, true);
|
|
274
115
|
}
|
|
275
116
|
|
|
276
117
|
}
|
|
@@ -415,7 +256,7 @@ class TxParameters {
|
|
|
415
256
|
this.FVersionsMatch = value;
|
|
416
257
|
this.hasVersionsMatch = true;
|
|
417
258
|
}
|
|
418
|
-
|
|
259
|
+
|
|
419
260
|
get versionRules() {
|
|
420
261
|
return this.FVersionRules;
|
|
421
262
|
}
|
|
@@ -432,19 +273,225 @@ e
|
|
|
432
273
|
return new TxParameters(langDefs);
|
|
433
274
|
}
|
|
434
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Process a single parameter. This is the single place where parameter names
|
|
278
|
+
* are interpreted; both the request Parameters resource (see readParams) and
|
|
279
|
+
* the valueset-expansion-parameter extension route through here.
|
|
280
|
+
*
|
|
281
|
+
* @param {string} name - the parameter name
|
|
282
|
+
* @param {Object} value - any element getValuePrimitive() understands: a
|
|
283
|
+
* Parameters.parameter entry, or the 'value' sub-extension of a
|
|
284
|
+
* valueset-expansion-parameter extension
|
|
285
|
+
* @param {boolean} overwrite - true when the parameter comes from the request
|
|
286
|
+
* (later values win); false when it comes from a ValueSet's embedded
|
|
287
|
+
* expansion parameters, which must not clobber what the request set
|
|
288
|
+
* explicitly. Parameters that accumulate (version rules, designations,
|
|
289
|
+
* properties, supplements) are unaffected - they always add.
|
|
290
|
+
*/
|
|
435
291
|
seeParameter(name, value, overwrite) {
|
|
436
|
-
if (value) {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
292
|
+
if (!name || !value) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// returns false if this single-valued parameter has already been set
|
|
297
|
+
// explicitly and we're not allowed to overwrite it
|
|
298
|
+
const claim = (key) => {
|
|
299
|
+
if (!overwrite && this.FSeen.has(key)) {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
this.FSeen.add(key);
|
|
303
|
+
return true;
|
|
304
|
+
};
|
|
305
|
+
// string ('true') and boolean (valueBoolean) forms are both accepted, since
|
|
306
|
+
// GET parameters always arrive as valueString
|
|
307
|
+
const asBool = () => strToBool(getValuePrimitive(value), false);
|
|
308
|
+
|
|
309
|
+
switch (name) {
|
|
310
|
+
// -- accumulating parameters: version rules --------------------------
|
|
311
|
+
case 'system-version': {
|
|
312
|
+
this.seeVersionRule(getValuePrimitive(value), false, 'default');
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
case 'check-system-version': {
|
|
316
|
+
this.seeVersionRule(getValuePrimitive(value), false, 'check');
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
case 'force-system-version': {
|
|
320
|
+
this.seeVersionRule(getValuePrimitive(value), false, 'override');
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
case 'default-valueset-version': {
|
|
324
|
+
this.seeVersionRule(getValuePrimitive(value), true, 'default');
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
case 'force-valueset-version': {
|
|
328
|
+
this.seeVersionRule(getValuePrimitive(value), true, 'override');
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
case 'check-valueset-version': {
|
|
332
|
+
this.seeVersionRule(getValuePrimitive(value), true, 'check');
|
|
333
|
+
break;
|
|
440
334
|
}
|
|
441
335
|
|
|
442
|
-
|
|
336
|
+
// -- other accumulating parameters -----------------------------------
|
|
337
|
+
case 'designation': {
|
|
443
338
|
this.designations.push(getValuePrimitive(value));
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
case 'property': {
|
|
342
|
+
// properties can arrive from the request, from an expansion parameter
|
|
343
|
+
// extension, and (R6) from ValueSet.compose.property, so de-duplicate:
|
|
344
|
+
// a repeat would emit the property twice and change the cache key
|
|
345
|
+
const prop = getValuePrimitive(value);
|
|
346
|
+
if (prop && !this.properties.includes(prop)) {
|
|
347
|
+
this.properties.push(prop);
|
|
348
|
+
}
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
case 'useSupplement': {
|
|
352
|
+
this.supplements.add(getValuePrimitive(value));
|
|
353
|
+
break;
|
|
444
354
|
}
|
|
445
355
|
|
|
446
|
-
|
|
447
|
-
|
|
356
|
+
// -- single valued parameters ----------------------------------------
|
|
357
|
+
case 'displayLanguage': {
|
|
358
|
+
if (claim('displayLanguage')) {
|
|
359
|
+
const lang = getValuePrimitive(value);
|
|
360
|
+
try {
|
|
361
|
+
this.DisplayLanguages = Languages.fromAcceptLanguage(lang, this.languageDefinitions, !this.validating);
|
|
362
|
+
if (lang) this.FHasDisplayLanguages = true;
|
|
363
|
+
} catch (error) {
|
|
364
|
+
throw new Issue("error", "processing", null, 'INVALID_DISPLAY_NAME', this.i18n.translate('INVALID_DISPLAY_NAME', this.HTTPLanguages, [lang]), "invalid-display").handleAsOO(400);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
case 'no-cache': {
|
|
370
|
+
// Write FUid (the field the cache key reads via hashSource); writing
|
|
371
|
+
// `this.uid` was a no-op so no-cache=true never busted the cache.
|
|
372
|
+
if (claim('no-cache') && asBool()) this.FUid = crypto.randomUUID();
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
case '_incomplete':
|
|
376
|
+
case 'limitedExpansion': {
|
|
377
|
+
if (claim('limitedExpansion')) this.limitedExpansion = asBool();
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
case 'includeDesignations': {
|
|
381
|
+
if (claim('includeDesignations')) this.includeDesignations = asBool();
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
case 'includeDefinition': {
|
|
385
|
+
if (claim('includeDefinition')) this.includeDefinition = asBool();
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
case 'activeOnly': {
|
|
389
|
+
if (claim('activeOnly')) this.activeOnly = asBool();
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
case 'excludeNested': {
|
|
393
|
+
if (claim('excludeNested')) this.excludeNested = asBool();
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
case 'excludeNotForUI': {
|
|
397
|
+
if (claim('excludeNotForUI')) this.excludeNotForUI = asBool();
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
case 'excludePostCoordinated': {
|
|
401
|
+
if (claim('excludePostCoordinated')) this.excludePostCoordinated = asBool();
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
case 'default-to-latest-version': {
|
|
405
|
+
if (claim('default-to-latest-version')) this.defaultToLatestVersion = asBool();
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
case 'incomplete-ok': {
|
|
409
|
+
if (claim('incomplete-ok')) this.incompleteOK = asBool();
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
case 'diagnostics': {
|
|
413
|
+
if (claim('diagnostics')) this.diagnostics = asBool();
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
case 'lenient-display-validation': {
|
|
417
|
+
if (claim('lenient-display-validation')) this.displayWarning = asBool();
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
case 'valueset-membership-only': {
|
|
421
|
+
if (claim('valueset-membership-only')) this.membershipOnly = asBool();
|
|
422
|
+
break;
|
|
423
|
+
}
|
|
424
|
+
case 'versionsMatch': {
|
|
425
|
+
if (claim('versionsMatch')) this.versionsMatch = asBool();
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
case 'inferSystem': {
|
|
429
|
+
if (claim('inferSystem')) this.inferSystem = asBool();
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
case 'abstract': {
|
|
433
|
+
// tri-state: only an explicit true/false changes the default (true)
|
|
434
|
+
const v = getValuePrimitive(value);
|
|
435
|
+
if (v === true || v === 'true') {
|
|
436
|
+
if (claim('abstract')) this.abstractOk = true;
|
|
437
|
+
} else if (v === false || v === 'false') {
|
|
438
|
+
if (claim('abstract')) this.abstractOk = false;
|
|
439
|
+
}
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
case 'term': // jQuery support
|
|
443
|
+
case 'filter': {
|
|
444
|
+
if (claim('filter')) this.filter = getValuePrimitive(value);
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
case 'count': {
|
|
448
|
+
if (claim('count')) this.count = Utilities.parseIntOrDefault(getValuePrimitive(value), -1);
|
|
449
|
+
break;
|
|
450
|
+
}
|
|
451
|
+
case 'offset': {
|
|
452
|
+
if (claim('offset')) this.offset = Utilities.parseIntOrDefault(getValuePrimitive(value), -1);
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
case 'limit': {
|
|
456
|
+
if (claim('limit')) this.limit = Utilities.parseIntOrDefault(getValuePrimitive(value), -1);
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
case 'sort': {
|
|
460
|
+
if (claim('sort')) this.sort = getValuePrimitive(value);
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// -- nested parameters -------------------------------------------------
|
|
465
|
+
case 'profile': {
|
|
466
|
+
// only meaningful from a Parameters.parameter, which can carry a resource
|
|
467
|
+
const res = value.resource;
|
|
468
|
+
if (res && (res.resourceType === 'Parameters' || res.resourceType === 'ExpansionProfile')) {
|
|
469
|
+
this.readParams(res);
|
|
470
|
+
}
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
case 'exclude-system': {
|
|
475
|
+
throw new Issue('error', 'not-supported', null, null, "The parameter 'exclude-system' is not supported by this system", null, 400);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Read parameters a ValueSet declares on its own compose. Presently that's
|
|
482
|
+
* R6's ValueSet.compose.property (0..* code) - the properties to return in
|
|
483
|
+
* the expansion, named by the value set rather than by the request. We don't
|
|
484
|
+
* formally support R6, but the element is harmless in earlier versions and
|
|
485
|
+
* honouring it costs nothing.
|
|
486
|
+
*
|
|
487
|
+
* @param {Object} compose - ValueSet.compose (raw JSON)
|
|
488
|
+
*/
|
|
489
|
+
seeCompose(compose) {
|
|
490
|
+
if (compose && Array.isArray(compose.property)) {
|
|
491
|
+
for (const prop of compose.property) {
|
|
492
|
+
if (typeof prop === 'string') {
|
|
493
|
+
this.seeParameter('property', {valueCode: prop}, false);
|
|
494
|
+
}
|
|
448
495
|
}
|
|
449
496
|
}
|
|
450
497
|
}
|
|
@@ -639,6 +686,10 @@ e
|
|
|
639
686
|
this.hasDisplayWarning = other.hasDisplayWarning;
|
|
640
687
|
this.sort = other.sort;
|
|
641
688
|
|
|
689
|
+
if (other.FSeen) {
|
|
690
|
+
this.FSeen = new Set(other.FSeen);
|
|
691
|
+
}
|
|
692
|
+
|
|
642
693
|
if (other.FProperties) {
|
|
643
694
|
this.FProperties = [...other.FProperties];
|
|
644
695
|
}
|