@health-samurai/react-components 0.0.0-alpha.18 → 0.0.0-alpha.20
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/LICENSE +21 -0
- package/dist/bundle.css +51 -33
- package/dist/src/components/code-editor/fhir-autocomplete.d.ts +70 -0
- package/dist/src/components/code-editor/fhir-autocomplete.d.ts.map +1 -0
- package/dist/src/components/code-editor/fhir-autocomplete.js +1849 -0
- package/dist/src/components/code-editor/fhir-autocomplete.js.map +1 -0
- package/dist/src/components/code-editor/fhir-autocomplete.test.js +1099 -0
- package/dist/src/components/code-editor/fhir-autocomplete.test.js.map +1 -0
- package/dist/src/components/code-editor/http/index.d.ts +9 -1
- package/dist/src/components/code-editor/http/index.d.ts.map +1 -1
- package/dist/src/components/code-editor/http/index.js +423 -3
- package/dist/src/components/code-editor/http/index.js.map +1 -1
- package/dist/src/components/code-editor/index.d.ts +13 -4
- package/dist/src/components/code-editor/index.d.ts.map +1 -1
- package/dist/src/components/code-editor/index.js +505 -96
- package/dist/src/components/code-editor/index.js.map +1 -1
- package/dist/src/components/code-editor/json-ast.d.ts +46 -0
- package/dist/src/components/code-editor/json-ast.d.ts.map +1 -0
- package/dist/src/components/code-editor/json-ast.js +465 -0
- package/dist/src/components/code-editor/json-ast.js.map +1 -0
- package/dist/src/components/code-editor/json-ast.test.js +206 -0
- package/dist/src/components/code-editor/json-ast.test.js.map +1 -0
- package/dist/src/components/code-editor/sql-completion.d.ts +22 -0
- package/dist/src/components/code-editor/sql-completion.d.ts.map +1 -0
- package/dist/src/components/code-editor/sql-completion.js +895 -0
- package/dist/src/components/code-editor/sql-completion.js.map +1 -0
- package/dist/src/components/date-picker-input.d.ts +10 -0
- package/dist/src/components/date-picker-input.d.ts.map +1 -0
- package/dist/src/components/date-picker-input.js +90 -0
- package/dist/src/components/date-picker-input.js.map +1 -0
- package/dist/src/components/date-picker-input.stories.js +76 -0
- package/dist/src/components/date-picker-input.stories.js.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/shadcn/components/ui/alert-dialog.d.ts +1 -1
- package/dist/src/shadcn/components/ui/calendar.d.ts +1 -1
- package/dist/src/shadcn/components/ui/carousel.d.ts +1 -1
- package/dist/src/shadcn/components/ui/chart.d.ts +3 -3
- package/dist/src/shadcn/components/ui/chart.d.ts.map +1 -1
- package/dist/src/shadcn/components/ui/chart.js +1 -1
- package/dist/src/shadcn/components/ui/chart.js.map +1 -1
- package/dist/src/shadcn/components/ui/command.d.ts +1 -1
- package/dist/src/shadcn/components/ui/pagination.d.ts +1 -1
- package/dist/src/shadcn/components/ui/resizable.stories.js +2 -2
- package/dist/src/shadcn/components/ui/resizable.stories.js.map +1 -1
- package/dist/src/shadcn/components/ui/sidebar.d.ts +4 -4
- package/dist/src/shadcn/components/ui/tabs.d.ts +3 -1
- package/dist/src/shadcn/components/ui/tabs.d.ts.map +1 -1
- package/dist/src/shadcn/components/ui/tabs.js +129 -2
- package/dist/src/shadcn/components/ui/tabs.js.map +1 -1
- package/dist/src/shadcn/components/ui/tabs.stories.js +1 -1
- package/dist/src/shadcn/components/ui/tabs.stories.js.map +1 -1
- package/dist/src/shadcn/components/ui/toggle-group.d.ts +1 -1
- package/dist/src/typography.css +1 -1
- package/package.json +24 -19
- package/src/components/code-editor/fhir-autocomplete.test.ts +993 -0
- package/src/components/code-editor/fhir-autocomplete.ts +2321 -0
- package/src/components/code-editor/http/index.ts +339 -2
- package/src/components/code-editor/index.tsx +593 -102
- package/src/components/code-editor/json-ast.test.ts +230 -0
- package/src/components/code-editor/json-ast.ts +590 -0
- package/src/components/code-editor/sql-completion.ts +1105 -0
- package/src/components/date-picker-input.stories.tsx +79 -0
- package/src/components/date-picker-input.tsx +104 -0
- package/src/index.tsx +1 -0
- package/src/shadcn/components/ui/chart.tsx +6 -3
- package/src/shadcn/components/ui/resizable.stories.tsx +2 -2
- package/src/shadcn/components/ui/tabs.stories.tsx +1 -1
- package/src/shadcn/components/ui/tabs.tsx +160 -2
- package/src/typography.css +1 -1
- package/dist/src/components/code-editor/http/grammar/http.test.d.ts +0 -2
- package/dist/src/components/code-editor/http/grammar/http.test.d.ts.map +0 -1
|
@@ -0,0 +1,1099 @@
|
|
|
1
|
+
import { CompletionContext } from "@codemirror/autocomplete";
|
|
2
|
+
import { json } from "@codemirror/lang-json";
|
|
3
|
+
import { EditorState } from "@codemirror/state";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import { buildParameterSnippet, jsonCompletionSource } from "./fhir-autocomplete.js";
|
|
6
|
+
// ── Minimal mock SDs ───────────────────────────────────────────────────
|
|
7
|
+
const PATIENT_SD = {
|
|
8
|
+
type: "Patient",
|
|
9
|
+
url: "http://hl7.org/fhir/StructureDefinition/Patient",
|
|
10
|
+
baseDefinition: "http://hl7.org/fhir/StructureDefinition/DomainResource",
|
|
11
|
+
differential: {
|
|
12
|
+
element: [
|
|
13
|
+
{
|
|
14
|
+
path: "Patient",
|
|
15
|
+
min: 0,
|
|
16
|
+
max: "*"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
path: "Patient.name",
|
|
20
|
+
min: 0,
|
|
21
|
+
max: "*",
|
|
22
|
+
type: [
|
|
23
|
+
{
|
|
24
|
+
code: "HumanName"
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
path: "Patient.gender",
|
|
30
|
+
min: 0,
|
|
31
|
+
max: "1",
|
|
32
|
+
type: [
|
|
33
|
+
{
|
|
34
|
+
code: "code"
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
binding: {
|
|
38
|
+
valueSet: "http://hl7.org/fhir/ValueSet/administrative-gender",
|
|
39
|
+
strength: "required"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
path: "Patient.birthDate",
|
|
44
|
+
min: 0,
|
|
45
|
+
max: "1",
|
|
46
|
+
type: [
|
|
47
|
+
{
|
|
48
|
+
code: "date"
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
path: "Patient.active",
|
|
54
|
+
min: 0,
|
|
55
|
+
max: "1",
|
|
56
|
+
type: [
|
|
57
|
+
{
|
|
58
|
+
code: "boolean"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
path: "Patient.managingOrganization",
|
|
64
|
+
min: 0,
|
|
65
|
+
max: "1",
|
|
66
|
+
type: [
|
|
67
|
+
{
|
|
68
|
+
code: "Reference",
|
|
69
|
+
targetProfile: [
|
|
70
|
+
"http://hl7.org/fhir/StructureDefinition/Organization"
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
path: "Patient.contained",
|
|
77
|
+
min: 0,
|
|
78
|
+
max: "*",
|
|
79
|
+
type: [
|
|
80
|
+
{
|
|
81
|
+
code: "Resource"
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
path: "Patient.meta",
|
|
87
|
+
min: 0,
|
|
88
|
+
max: "1",
|
|
89
|
+
type: [
|
|
90
|
+
{
|
|
91
|
+
code: "Meta"
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const OBSERVATION_SD = {
|
|
99
|
+
type: "Observation",
|
|
100
|
+
url: "http://hl7.org/fhir/StructureDefinition/Observation",
|
|
101
|
+
baseDefinition: "http://hl7.org/fhir/StructureDefinition/DomainResource",
|
|
102
|
+
differential: {
|
|
103
|
+
element: [
|
|
104
|
+
{
|
|
105
|
+
path: "Observation",
|
|
106
|
+
min: 0,
|
|
107
|
+
max: "*"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
path: "Observation.status",
|
|
111
|
+
min: 1,
|
|
112
|
+
max: "1",
|
|
113
|
+
type: [
|
|
114
|
+
{
|
|
115
|
+
code: "code"
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
path: "Observation.code",
|
|
121
|
+
min: 1,
|
|
122
|
+
max: "1",
|
|
123
|
+
type: [
|
|
124
|
+
{
|
|
125
|
+
code: "CodeableConcept"
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
path: "Observation.subject",
|
|
131
|
+
min: 0,
|
|
132
|
+
max: "1",
|
|
133
|
+
type: [
|
|
134
|
+
{
|
|
135
|
+
code: "Reference",
|
|
136
|
+
targetProfile: [
|
|
137
|
+
"http://hl7.org/fhir/StructureDefinition/Patient",
|
|
138
|
+
"http://hl7.org/fhir/StructureDefinition/Group"
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const DOMAIN_RESOURCE_SD = {
|
|
147
|
+
type: "DomainResource",
|
|
148
|
+
url: "http://hl7.org/fhir/StructureDefinition/DomainResource",
|
|
149
|
+
baseDefinition: "http://hl7.org/fhir/StructureDefinition/Resource",
|
|
150
|
+
differential: {
|
|
151
|
+
element: [
|
|
152
|
+
{
|
|
153
|
+
path: "DomainResource",
|
|
154
|
+
min: 0,
|
|
155
|
+
max: "*"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
path: "DomainResource.text",
|
|
159
|
+
min: 0,
|
|
160
|
+
max: "1",
|
|
161
|
+
type: [
|
|
162
|
+
{
|
|
163
|
+
code: "Narrative"
|
|
164
|
+
}
|
|
165
|
+
]
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
path: "DomainResource.contained",
|
|
169
|
+
min: 0,
|
|
170
|
+
max: "*",
|
|
171
|
+
type: [
|
|
172
|
+
{
|
|
173
|
+
code: "Resource"
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
path: "DomainResource.extension",
|
|
179
|
+
min: 0,
|
|
180
|
+
max: "*",
|
|
181
|
+
type: [
|
|
182
|
+
{
|
|
183
|
+
code: "Extension"
|
|
184
|
+
}
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
path: "DomainResource.modifierExtension",
|
|
189
|
+
min: 0,
|
|
190
|
+
max: "*",
|
|
191
|
+
type: [
|
|
192
|
+
{
|
|
193
|
+
code: "Extension"
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
const RESOURCE_SD = {
|
|
201
|
+
type: "Resource",
|
|
202
|
+
url: "http://hl7.org/fhir/StructureDefinition/Resource",
|
|
203
|
+
differential: {
|
|
204
|
+
element: [
|
|
205
|
+
{
|
|
206
|
+
path: "Resource",
|
|
207
|
+
min: 0,
|
|
208
|
+
max: "*"
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
path: "Resource.id",
|
|
212
|
+
min: 0,
|
|
213
|
+
max: "1",
|
|
214
|
+
type: [
|
|
215
|
+
{
|
|
216
|
+
code: "id"
|
|
217
|
+
}
|
|
218
|
+
]
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
path: "Resource.meta",
|
|
222
|
+
min: 0,
|
|
223
|
+
max: "1",
|
|
224
|
+
type: [
|
|
225
|
+
{
|
|
226
|
+
code: "Meta"
|
|
227
|
+
}
|
|
228
|
+
]
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
const HUMAN_NAME_SD = {
|
|
234
|
+
type: "HumanName",
|
|
235
|
+
url: "http://hl7.org/fhir/StructureDefinition/HumanName",
|
|
236
|
+
differential: {
|
|
237
|
+
element: [
|
|
238
|
+
{
|
|
239
|
+
path: "HumanName",
|
|
240
|
+
min: 0,
|
|
241
|
+
max: "*"
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
path: "HumanName.family",
|
|
245
|
+
min: 0,
|
|
246
|
+
max: "1",
|
|
247
|
+
type: [
|
|
248
|
+
{
|
|
249
|
+
code: "string"
|
|
250
|
+
}
|
|
251
|
+
]
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
path: "HumanName.given",
|
|
255
|
+
min: 0,
|
|
256
|
+
max: "*",
|
|
257
|
+
type: [
|
|
258
|
+
{
|
|
259
|
+
code: "string"
|
|
260
|
+
}
|
|
261
|
+
]
|
|
262
|
+
}
|
|
263
|
+
]
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
const REFERENCE_SD = {
|
|
267
|
+
type: "Reference",
|
|
268
|
+
url: "http://hl7.org/fhir/StructureDefinition/Reference",
|
|
269
|
+
differential: {
|
|
270
|
+
element: [
|
|
271
|
+
{
|
|
272
|
+
path: "Reference",
|
|
273
|
+
min: 0,
|
|
274
|
+
max: "*"
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
path: "Reference.reference",
|
|
278
|
+
min: 0,
|
|
279
|
+
max: "1",
|
|
280
|
+
type: [
|
|
281
|
+
{
|
|
282
|
+
code: "string"
|
|
283
|
+
}
|
|
284
|
+
]
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
path: "Reference.display",
|
|
288
|
+
min: 0,
|
|
289
|
+
max: "1",
|
|
290
|
+
type: [
|
|
291
|
+
{
|
|
292
|
+
code: "string"
|
|
293
|
+
}
|
|
294
|
+
]
|
|
295
|
+
}
|
|
296
|
+
]
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
const META_SD = {
|
|
300
|
+
type: "Meta",
|
|
301
|
+
url: "http://hl7.org/fhir/StructureDefinition/Meta",
|
|
302
|
+
differential: {
|
|
303
|
+
element: [
|
|
304
|
+
{
|
|
305
|
+
path: "Meta",
|
|
306
|
+
min: 0,
|
|
307
|
+
max: "*"
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
path: "Meta.profile",
|
|
311
|
+
min: 0,
|
|
312
|
+
max: "*",
|
|
313
|
+
type: [
|
|
314
|
+
{
|
|
315
|
+
code: "canonical",
|
|
316
|
+
targetProfile: [
|
|
317
|
+
"http://hl7.org/fhir/StructureDefinition/StructureDefinition"
|
|
318
|
+
]
|
|
319
|
+
}
|
|
320
|
+
]
|
|
321
|
+
}
|
|
322
|
+
]
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
const BUNDLE_SD = {
|
|
326
|
+
type: "Bundle",
|
|
327
|
+
url: "http://hl7.org/fhir/StructureDefinition/Bundle",
|
|
328
|
+
baseDefinition: "http://hl7.org/fhir/StructureDefinition/Resource",
|
|
329
|
+
differential: {
|
|
330
|
+
element: [
|
|
331
|
+
{
|
|
332
|
+
path: "Bundle",
|
|
333
|
+
min: 0,
|
|
334
|
+
max: "*"
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
path: "Bundle.type",
|
|
338
|
+
min: 1,
|
|
339
|
+
max: "1",
|
|
340
|
+
type: [
|
|
341
|
+
{
|
|
342
|
+
code: "code"
|
|
343
|
+
}
|
|
344
|
+
]
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
path: "Bundle.entry",
|
|
348
|
+
min: 0,
|
|
349
|
+
max: "*",
|
|
350
|
+
type: [
|
|
351
|
+
{
|
|
352
|
+
code: "BackboneElement"
|
|
353
|
+
}
|
|
354
|
+
]
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
path: "Bundle.entry.resource",
|
|
358
|
+
min: 0,
|
|
359
|
+
max: "1",
|
|
360
|
+
type: [
|
|
361
|
+
{
|
|
362
|
+
code: "Resource"
|
|
363
|
+
}
|
|
364
|
+
]
|
|
365
|
+
}
|
|
366
|
+
]
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
const PARAMETERS_SD = {
|
|
370
|
+
type: "Parameters",
|
|
371
|
+
url: "http://hl7.org/fhir/StructureDefinition/Parameters",
|
|
372
|
+
baseDefinition: "http://hl7.org/fhir/StructureDefinition/Resource",
|
|
373
|
+
differential: {
|
|
374
|
+
element: [
|
|
375
|
+
{
|
|
376
|
+
path: "Parameters",
|
|
377
|
+
min: 0,
|
|
378
|
+
max: "*"
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
path: "Parameters.parameter",
|
|
382
|
+
min: 0,
|
|
383
|
+
max: "*",
|
|
384
|
+
type: [
|
|
385
|
+
{
|
|
386
|
+
code: "BackboneElement"
|
|
387
|
+
}
|
|
388
|
+
]
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
path: "Parameters.parameter.name",
|
|
392
|
+
min: 1,
|
|
393
|
+
max: "1",
|
|
394
|
+
type: [
|
|
395
|
+
{
|
|
396
|
+
code: "string"
|
|
397
|
+
}
|
|
398
|
+
]
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
path: "Parameters.parameter.value[x]",
|
|
402
|
+
min: 0,
|
|
403
|
+
max: "1",
|
|
404
|
+
type: [
|
|
405
|
+
{
|
|
406
|
+
code: "string"
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
code: "boolean"
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
code: "integer"
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
code: "code"
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
code: "Reference"
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
code: "CodeableConcept"
|
|
422
|
+
}
|
|
423
|
+
]
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
path: "Parameters.parameter.resource",
|
|
427
|
+
min: 0,
|
|
428
|
+
max: "1",
|
|
429
|
+
type: [
|
|
430
|
+
{
|
|
431
|
+
code: "Resource"
|
|
432
|
+
}
|
|
433
|
+
]
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
path: "Parameters.parameter.part",
|
|
437
|
+
min: 0,
|
|
438
|
+
max: "*",
|
|
439
|
+
contentReference: "#Parameters.parameter"
|
|
440
|
+
}
|
|
441
|
+
]
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
const INSTALL_PARAMS_PROFILE = {
|
|
445
|
+
type: "Parameters",
|
|
446
|
+
url: "http://health-samurai.io/fhir/core/StructureDefinition/fhir-package-install-parameters",
|
|
447
|
+
baseDefinition: "http://hl7.org/fhir/StructureDefinition/Parameters",
|
|
448
|
+
differential: {
|
|
449
|
+
element: [
|
|
450
|
+
{
|
|
451
|
+
path: "Parameters.parameter",
|
|
452
|
+
min: 1
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
path: "Parameters.parameter",
|
|
456
|
+
sliceName: "package",
|
|
457
|
+
min: 1,
|
|
458
|
+
max: "*"
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
path: "Parameters.parameter.name",
|
|
462
|
+
fixedString: "package"
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
path: "Parameters.parameter",
|
|
466
|
+
sliceName: "registry",
|
|
467
|
+
min: 0,
|
|
468
|
+
max: "1"
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
path: "Parameters.parameter.name",
|
|
472
|
+
fixedString: "registry"
|
|
473
|
+
}
|
|
474
|
+
]
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
const TYPED_PARAMS_PROFILE = {
|
|
478
|
+
type: "Parameters",
|
|
479
|
+
url: "http://example.com/StructureDefinition/typed-params",
|
|
480
|
+
baseDefinition: "http://hl7.org/fhir/StructureDefinition/Parameters",
|
|
481
|
+
differential: {
|
|
482
|
+
element: [
|
|
483
|
+
{
|
|
484
|
+
path: "Parameters.parameter",
|
|
485
|
+
sliceName: "count",
|
|
486
|
+
min: 1,
|
|
487
|
+
max: "1"
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
path: "Parameters.parameter.name",
|
|
491
|
+
fixedString: "count"
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
path: "Parameters.parameter.value[x]",
|
|
495
|
+
type: [
|
|
496
|
+
{
|
|
497
|
+
code: "integer"
|
|
498
|
+
}
|
|
499
|
+
]
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
path: "Parameters.parameter",
|
|
503
|
+
sliceName: "label",
|
|
504
|
+
min: 0,
|
|
505
|
+
max: "1"
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
path: "Parameters.parameter.name",
|
|
509
|
+
fixedString: "label"
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
path: "Parameters.parameter.value[x]",
|
|
513
|
+
type: [
|
|
514
|
+
{
|
|
515
|
+
code: "string"
|
|
516
|
+
}
|
|
517
|
+
]
|
|
518
|
+
}
|
|
519
|
+
]
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
const TOPIC_DEST_SD = {
|
|
523
|
+
type: "AidboxTopicDestination",
|
|
524
|
+
url: "http://aidbox.app/StructureDefinition/AidboxTopicDestination",
|
|
525
|
+
baseDefinition: "http://hl7.org/fhir/StructureDefinition/Parameters",
|
|
526
|
+
differential: {
|
|
527
|
+
element: [
|
|
528
|
+
{
|
|
529
|
+
path: "AidboxTopicDestination",
|
|
530
|
+
min: 0,
|
|
531
|
+
max: "*"
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
path: "AidboxTopicDestination.kind",
|
|
535
|
+
min: 0,
|
|
536
|
+
max: "1",
|
|
537
|
+
type: [
|
|
538
|
+
{
|
|
539
|
+
code: "string"
|
|
540
|
+
}
|
|
541
|
+
]
|
|
542
|
+
}
|
|
543
|
+
]
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
const TOPIC_DEST_KAFKA_PROFILE = {
|
|
547
|
+
type: "AidboxTopicDestination",
|
|
548
|
+
url: "http://aidbox.app/StructureDefinition/aidboxtopicdestination-kafka-best-effort",
|
|
549
|
+
baseDefinition: "http://aidbox.app/StructureDefinition/AidboxTopicDestination",
|
|
550
|
+
differential: {
|
|
551
|
+
element: [
|
|
552
|
+
{
|
|
553
|
+
path: "AidboxTopicDestination.kind",
|
|
554
|
+
fixedString: "kafka-best-effort"
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
path: "AidboxTopicDestination.parameter",
|
|
558
|
+
sliceName: "kafkaTopic",
|
|
559
|
+
min: 1,
|
|
560
|
+
max: "1"
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
path: "AidboxTopicDestination.parameter.name",
|
|
564
|
+
fixedString: "kafkaTopic"
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
path: "AidboxTopicDestination.parameter.value[x]",
|
|
568
|
+
type: [
|
|
569
|
+
{
|
|
570
|
+
code: "string"
|
|
571
|
+
}
|
|
572
|
+
]
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
path: "AidboxTopicDestination.parameter",
|
|
576
|
+
sliceName: "bootstrapServers",
|
|
577
|
+
min: 1,
|
|
578
|
+
max: "1"
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
path: "AidboxTopicDestination.parameter.name",
|
|
582
|
+
fixedString: "bootstrapServers"
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
path: "AidboxTopicDestination.parameter.value[x]",
|
|
586
|
+
type: [
|
|
587
|
+
{
|
|
588
|
+
code: "string"
|
|
589
|
+
}
|
|
590
|
+
]
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
path: "AidboxTopicDestination.parameter",
|
|
594
|
+
sliceName: "batchSize",
|
|
595
|
+
min: 0,
|
|
596
|
+
max: "1"
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
path: "AidboxTopicDestination.parameter.name",
|
|
600
|
+
fixedString: "batchSize"
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
path: "AidboxTopicDestination.parameter.value[x]",
|
|
604
|
+
type: [
|
|
605
|
+
{
|
|
606
|
+
code: "integer"
|
|
607
|
+
}
|
|
608
|
+
]
|
|
609
|
+
}
|
|
610
|
+
]
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
const RESOURCE_TYPE_LIST = [
|
|
614
|
+
{
|
|
615
|
+
type: "Patient"
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
type: "Observation"
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
type: "Organization"
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
type: "Bundle"
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
type: "Parameters"
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
type: "AidboxTopicDestination"
|
|
631
|
+
}
|
|
632
|
+
];
|
|
633
|
+
const ALL_SDS = {
|
|
634
|
+
Patient: PATIENT_SD,
|
|
635
|
+
Observation: OBSERVATION_SD,
|
|
636
|
+
DomainResource: DOMAIN_RESOURCE_SD,
|
|
637
|
+
Resource: RESOURCE_SD,
|
|
638
|
+
HumanName: HUMAN_NAME_SD,
|
|
639
|
+
Reference: REFERENCE_SD,
|
|
640
|
+
Meta: META_SD,
|
|
641
|
+
Bundle: BUNDLE_SD,
|
|
642
|
+
Parameters: PARAMETERS_SD,
|
|
643
|
+
"http://hl7.org/fhir/StructureDefinition/Patient": PATIENT_SD,
|
|
644
|
+
"http://hl7.org/fhir/StructureDefinition/Observation": OBSERVATION_SD,
|
|
645
|
+
"http://hl7.org/fhir/StructureDefinition/DomainResource": DOMAIN_RESOURCE_SD,
|
|
646
|
+
"http://hl7.org/fhir/StructureDefinition/Resource": RESOURCE_SD,
|
|
647
|
+
"http://hl7.org/fhir/StructureDefinition/HumanName": HUMAN_NAME_SD,
|
|
648
|
+
"http://hl7.org/fhir/StructureDefinition/Reference": REFERENCE_SD,
|
|
649
|
+
"http://hl7.org/fhir/StructureDefinition/Meta": META_SD,
|
|
650
|
+
"http://hl7.org/fhir/StructureDefinition/Bundle": BUNDLE_SD,
|
|
651
|
+
"http://hl7.org/fhir/StructureDefinition/Parameters": PARAMETERS_SD,
|
|
652
|
+
"http://health-samurai.io/fhir/core/StructureDefinition/fhir-package-install-parameters": INSTALL_PARAMS_PROFILE,
|
|
653
|
+
"http://example.com/StructureDefinition/typed-params": TYPED_PARAMS_PROFILE,
|
|
654
|
+
AidboxTopicDestination: TOPIC_DEST_SD,
|
|
655
|
+
"http://aidbox.app/StructureDefinition/AidboxTopicDestination": TOPIC_DEST_SD,
|
|
656
|
+
"http://aidbox.app/StructureDefinition/aidboxtopicdestination-kafka-best-effort": TOPIC_DEST_KAFKA_PROFILE
|
|
657
|
+
};
|
|
658
|
+
// ── Mock getSDs ────────────────────────────────────────────────────────
|
|
659
|
+
const mockGetSDs = async (params)=>{
|
|
660
|
+
if (params.kind === "resource" && params.derivation === "specialization") {
|
|
661
|
+
return RESOURCE_TYPE_LIST;
|
|
662
|
+
}
|
|
663
|
+
if (params.url) {
|
|
664
|
+
const sd = ALL_SDS[params.url];
|
|
665
|
+
return sd ? [
|
|
666
|
+
sd
|
|
667
|
+
] : [];
|
|
668
|
+
}
|
|
669
|
+
if (params.type && params.derivation === "specialization") {
|
|
670
|
+
const sd = ALL_SDS[params.type];
|
|
671
|
+
return sd ? [
|
|
672
|
+
sd
|
|
673
|
+
] : [];
|
|
674
|
+
}
|
|
675
|
+
if (params.type && params["derivation:missing"] === "true") {
|
|
676
|
+
const sd = ALL_SDS[params.type];
|
|
677
|
+
return sd ? [
|
|
678
|
+
sd
|
|
679
|
+
] : [];
|
|
680
|
+
}
|
|
681
|
+
if (params.type === "Extension") {
|
|
682
|
+
return [];
|
|
683
|
+
}
|
|
684
|
+
return [];
|
|
685
|
+
};
|
|
686
|
+
const mockExpandValueSet = async (url, _filter)=>{
|
|
687
|
+
if (url === "http://hl7.org/fhir/ValueSet/administrative-gender") {
|
|
688
|
+
return [
|
|
689
|
+
{
|
|
690
|
+
code: "male",
|
|
691
|
+
display: "Male"
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
code: "female",
|
|
695
|
+
display: "Female"
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
code: "other",
|
|
699
|
+
display: "Other"
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
code: "unknown",
|
|
703
|
+
display: "Unknown"
|
|
704
|
+
}
|
|
705
|
+
];
|
|
706
|
+
}
|
|
707
|
+
return [];
|
|
708
|
+
};
|
|
709
|
+
// ── Test helpers ───────────────────────────────────────────────────────
|
|
710
|
+
function completionAt(doc, marker = "|") {
|
|
711
|
+
const pos = doc.indexOf(marker);
|
|
712
|
+
const text = doc.slice(0, pos) + doc.slice(pos + 1);
|
|
713
|
+
const state = EditorState.create({
|
|
714
|
+
doc: text,
|
|
715
|
+
extensions: [
|
|
716
|
+
json()
|
|
717
|
+
]
|
|
718
|
+
});
|
|
719
|
+
const cc = new CompletionContext(state, pos, true);
|
|
720
|
+
return {
|
|
721
|
+
state,
|
|
722
|
+
cc,
|
|
723
|
+
pos
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
function labels(result) {
|
|
727
|
+
return result?.options.map((o)=>o.label) ?? [];
|
|
728
|
+
}
|
|
729
|
+
// ── Caches persist across tests — clear between describes ──────────────
|
|
730
|
+
// The SD cache is module-level in fhir-autocomplete.ts.
|
|
731
|
+
// Since we use consistent mock data, the cache doesn't cause issues.
|
|
732
|
+
// ── Tests ──────────────────────────────────────────────────────────────
|
|
733
|
+
describe("fhir-autocomplete: jsonCompletionSource", ()=>{
|
|
734
|
+
const source = jsonCompletionSource(mockGetSDs, undefined, mockExpandValueSet);
|
|
735
|
+
describe("resourceType value completions", ()=>{
|
|
736
|
+
it("offers resource types in empty resourceType value", async ()=>{
|
|
737
|
+
const { cc } = completionAt('{\n "resourceType": "|\n}');
|
|
738
|
+
const result = await source(cc);
|
|
739
|
+
const l = labels(result);
|
|
740
|
+
expect(l).toContain("Patient");
|
|
741
|
+
expect(l).toContain("Observation");
|
|
742
|
+
expect(l).toContain("Organization");
|
|
743
|
+
});
|
|
744
|
+
});
|
|
745
|
+
describe("property completions", ()=>{
|
|
746
|
+
it("offers Patient fields when resourceType is set", async ()=>{
|
|
747
|
+
const { cc } = completionAt('{\n "resourceType": "Patient",\n |\n}');
|
|
748
|
+
const result = await source(cc);
|
|
749
|
+
const l = labels(result);
|
|
750
|
+
expect(l).toContain("name");
|
|
751
|
+
expect(l).toContain("gender");
|
|
752
|
+
expect(l).toContain("birthDate");
|
|
753
|
+
expect(l).toContain("managingOrganization");
|
|
754
|
+
});
|
|
755
|
+
it("offers resourceType when no resourceType is set", async ()=>{
|
|
756
|
+
const { cc } = completionAt("{\n |\n}");
|
|
757
|
+
const result = await source(cc);
|
|
758
|
+
const l = labels(result);
|
|
759
|
+
expect(l).toContain("resourceType");
|
|
760
|
+
});
|
|
761
|
+
it("offers primitive extensions (_birthDate)", async ()=>{
|
|
762
|
+
const { cc } = completionAt('{\n "resourceType": "Patient",\n |\n}');
|
|
763
|
+
const result = await source(cc);
|
|
764
|
+
const l = labels(result);
|
|
765
|
+
expect(l).toContain("_birthDate");
|
|
766
|
+
expect(l).toContain("_gender");
|
|
767
|
+
});
|
|
768
|
+
it("excludes properties already present in object", async ()=>{
|
|
769
|
+
const { cc } = completionAt('{\n "resourceType": "Patient",\n "gender": "male",\n |\n}');
|
|
770
|
+
const result = await source(cc);
|
|
771
|
+
const l = labels(result);
|
|
772
|
+
expect(l).toContain("name");
|
|
773
|
+
expect(l).toContain("birthDate");
|
|
774
|
+
expect(l).not.toContain("gender");
|
|
775
|
+
expect(l).not.toContain("resourceType");
|
|
776
|
+
});
|
|
777
|
+
it("does not offer property completions inside arrays", async ()=>{
|
|
778
|
+
const { cc } = completionAt('{\n "resourceType": "Patient",\n "name": [\n |\n ]\n}');
|
|
779
|
+
const result = await source(cc);
|
|
780
|
+
expect(result).toBe(null);
|
|
781
|
+
});
|
|
782
|
+
});
|
|
783
|
+
describe("property completions with resourceTypeHint", ()=>{
|
|
784
|
+
const hintSource = jsonCompletionSource(mockGetSDs, "Patient", mockExpandValueSet);
|
|
785
|
+
it("uses hint when resourceType is not in document", async ()=>{
|
|
786
|
+
const { cc } = completionAt("{\n |\n}");
|
|
787
|
+
const result = await hintSource(cc);
|
|
788
|
+
const l = labels(result);
|
|
789
|
+
expect(l).toContain("name");
|
|
790
|
+
expect(l).toContain("gender");
|
|
791
|
+
});
|
|
792
|
+
});
|
|
793
|
+
describe("terminology binding completions", ()=>{
|
|
794
|
+
it("offers gender codes for Patient.gender", async ()=>{
|
|
795
|
+
const { cc } = completionAt('{\n "resourceType": "Patient",\n "gender": "|\n}');
|
|
796
|
+
const result = await source(cc);
|
|
797
|
+
const l = labels(result);
|
|
798
|
+
expect(l).toContain("male");
|
|
799
|
+
expect(l).toContain("female");
|
|
800
|
+
expect(l).toContain("other");
|
|
801
|
+
expect(l).toContain("unknown");
|
|
802
|
+
});
|
|
803
|
+
});
|
|
804
|
+
describe("boolean value completions", ()=>{
|
|
805
|
+
it("offers true and false for boolean fields", async ()=>{
|
|
806
|
+
const { cc } = completionAt('{\n "resourceType": "Patient",\n "active": |\n}');
|
|
807
|
+
const result = await source(cc);
|
|
808
|
+
const l = labels(result);
|
|
809
|
+
expect(l).toContain("true");
|
|
810
|
+
expect(l).toContain("false");
|
|
811
|
+
expect(l).toHaveLength(2);
|
|
812
|
+
});
|
|
813
|
+
it("offers property completions (not booleans) on new line after boolean value", async ()=>{
|
|
814
|
+
const { cc } = completionAt('{\n "resourceType": "Patient",\n "active": true,\n |\n}');
|
|
815
|
+
const result = await source(cc);
|
|
816
|
+
const l = labels(result);
|
|
817
|
+
expect(l).not.toContain("true");
|
|
818
|
+
expect(l).not.toContain("false");
|
|
819
|
+
expect(l).toContain("name");
|
|
820
|
+
expect(l).toContain("gender");
|
|
821
|
+
});
|
|
822
|
+
});
|
|
823
|
+
describe("reference target completions", ()=>{
|
|
824
|
+
it("offers Organization/ for Patient.managingOrganization.reference", async ()=>{
|
|
825
|
+
const { cc } = completionAt('{\n "resourceType": "Patient",\n "managingOrganization": {\n "reference": "|\n }\n}');
|
|
826
|
+
const result = await source(cc);
|
|
827
|
+
const l = labels(result);
|
|
828
|
+
expect(l).toContain("Organization/");
|
|
829
|
+
});
|
|
830
|
+
it("offers Patient/ and Group/ for Observation.subject.reference", async ()=>{
|
|
831
|
+
const { cc } = completionAt('{\n "resourceType": "Observation",\n "subject": {\n "reference": "|\n }\n}');
|
|
832
|
+
const result = await source(cc);
|
|
833
|
+
const l = labels(result);
|
|
834
|
+
expect(l).toContain("Patient/");
|
|
835
|
+
expect(l).toContain("Group/");
|
|
836
|
+
});
|
|
837
|
+
});
|
|
838
|
+
describe("contained resource completions", ()=>{
|
|
839
|
+
it("offers resourceType inside contained array item", async ()=>{
|
|
840
|
+
const { cc } = completionAt('{\n "resourceType": "Patient",\n "contained": [\n {\n |\n }\n ]\n}');
|
|
841
|
+
const result = await source(cc);
|
|
842
|
+
const l = labels(result);
|
|
843
|
+
expect(l).toContain("resourceType");
|
|
844
|
+
});
|
|
845
|
+
it("offers inner resource fields when contained has resourceType", async ()=>{
|
|
846
|
+
const { cc } = completionAt('{\n "resourceType": "Patient",\n "contained": [\n {\n "resourceType": "Observation",\n |\n }\n ]\n}');
|
|
847
|
+
const result = await source(cc);
|
|
848
|
+
const l = labels(result);
|
|
849
|
+
expect(l).toContain("status");
|
|
850
|
+
expect(l).toContain("code");
|
|
851
|
+
expect(l).toContain("subject");
|
|
852
|
+
// Should NOT contain Patient fields
|
|
853
|
+
expect(l).not.toContain("gender");
|
|
854
|
+
expect(l).not.toContain("birthDate");
|
|
855
|
+
});
|
|
856
|
+
it("offers correct reference targets for contained Observation.subject", async ()=>{
|
|
857
|
+
const { cc } = completionAt('{\n "resourceType": "Patient",\n "contained": [\n {\n "resourceType": "Observation",\n "subject": {\n "reference": "|\n }\n }\n ]\n}');
|
|
858
|
+
const result = await source(cc);
|
|
859
|
+
const l = labels(result);
|
|
860
|
+
expect(l).toContain("Patient/");
|
|
861
|
+
expect(l).toContain("Group/");
|
|
862
|
+
// Should NOT contain Organization/ (that's from Patient.managingOrganization)
|
|
863
|
+
expect(l).not.toContain("Organization/");
|
|
864
|
+
});
|
|
865
|
+
});
|
|
866
|
+
describe("Bundle.entry.resource completions", ()=>{
|
|
867
|
+
it("offers Observation fields inside entry.resource with explicit Bundle resourceType", async ()=>{
|
|
868
|
+
const { cc } = completionAt('{\n "resourceType": "Bundle",\n "entry": [\n {\n "resource": {\n "resourceType": "Observation",\n |\n }\n }\n ]\n}');
|
|
869
|
+
const result = await source(cc);
|
|
870
|
+
const l = labels(result);
|
|
871
|
+
expect(l).toContain("status");
|
|
872
|
+
expect(l).toContain("code");
|
|
873
|
+
expect(l).toContain("subject");
|
|
874
|
+
expect(l).not.toContain("type");
|
|
875
|
+
});
|
|
876
|
+
it("offers Observation fields when Bundle resourceType comes from hint (URL)", async ()=>{
|
|
877
|
+
const hintSource = jsonCompletionSource(mockGetSDs, "Bundle", mockExpandValueSet);
|
|
878
|
+
const { cc } = completionAt('{\n "entry": [\n {\n "resource": {\n "resourceType": "Observation",\n |\n }\n }\n ]\n}');
|
|
879
|
+
const result = await hintSource(cc);
|
|
880
|
+
const l = labels(result);
|
|
881
|
+
expect(l).toContain("status");
|
|
882
|
+
expect(l).toContain("code");
|
|
883
|
+
expect(l).toContain("subject");
|
|
884
|
+
expect(l).not.toContain("type");
|
|
885
|
+
});
|
|
886
|
+
});
|
|
887
|
+
describe("nested object completions", ()=>{
|
|
888
|
+
it("offers HumanName fields inside name array item", async ()=>{
|
|
889
|
+
const { cc } = completionAt('{\n "resourceType": "Patient",\n "name": [\n {\n |\n }\n ]\n}');
|
|
890
|
+
const result = await source(cc);
|
|
891
|
+
const l = labels(result);
|
|
892
|
+
expect(l).toContain("family");
|
|
893
|
+
expect(l).toContain("given");
|
|
894
|
+
});
|
|
895
|
+
it("offers Reference fields inside managingOrganization", async ()=>{
|
|
896
|
+
const { cc } = completionAt('{\n "resourceType": "Patient",\n "managingOrganization": {\n |\n }\n}');
|
|
897
|
+
const result = await source(cc);
|
|
898
|
+
const l = labels(result);
|
|
899
|
+
expect(l).toContain("reference");
|
|
900
|
+
expect(l).toContain("display");
|
|
901
|
+
});
|
|
902
|
+
});
|
|
903
|
+
describe("Parameters completions", ()=>{
|
|
904
|
+
it("offers parameter fields inside parameter array item object", async ()=>{
|
|
905
|
+
const { cc } = completionAt('{\n "resourceType": "Parameters",\n "parameter": [\n {\n |\n }\n ]\n}');
|
|
906
|
+
const result = await source(cc);
|
|
907
|
+
const l = labels(result);
|
|
908
|
+
expect(l).toContain("name");
|
|
909
|
+
expect(l).toContain("valueString");
|
|
910
|
+
expect(l).toContain("valueBoolean");
|
|
911
|
+
expect(l).toContain("resource");
|
|
912
|
+
expect(l).toContain("part");
|
|
913
|
+
});
|
|
914
|
+
it("offers slice names for parameter.name from profile", async ()=>{
|
|
915
|
+
const { cc } = completionAt('{\n "resourceType": "Parameters",\n "meta": {\n "profile": ["http://health-samurai.io/fhir/core/StructureDefinition/fhir-package-install-parameters"]\n },\n "parameter": [\n {\n "name": "|\n }\n ]\n}');
|
|
916
|
+
const result = await source(cc);
|
|
917
|
+
const l = labels(result);
|
|
918
|
+
expect(l).toContain("package");
|
|
919
|
+
expect(l).toContain("registry");
|
|
920
|
+
});
|
|
921
|
+
it("offers parameter snippets in array-item position from profile", async ()=>{
|
|
922
|
+
const { cc } = completionAt('{\n "resourceType": "Parameters",\n "meta": {\n "profile": ["http://health-samurai.io/fhir/core/StructureDefinition/fhir-package-install-parameters"]\n },\n "parameter": [\n |\n ]\n}');
|
|
923
|
+
const result = await source(cc);
|
|
924
|
+
const l = labels(result);
|
|
925
|
+
expect(l).toContain("package");
|
|
926
|
+
expect(l).toContain("registry");
|
|
927
|
+
expect(l).toContain("parameter");
|
|
928
|
+
});
|
|
929
|
+
it("offers generic parameter template without profile", async ()=>{
|
|
930
|
+
const { cc } = completionAt('{\n "resourceType": "Parameters",\n "parameter": [\n |\n ]\n}');
|
|
931
|
+
const result = await source(cc);
|
|
932
|
+
expect(result).not.toBe(null);
|
|
933
|
+
const l = labels(result);
|
|
934
|
+
expect(l).toContain("parameter");
|
|
935
|
+
});
|
|
936
|
+
it("offers parameter fields for second array item", async ()=>{
|
|
937
|
+
const { cc } = completionAt('{\n "resourceType": "Parameters",\n "parameter": [\n {"name": "a", "valueString": "1"},\n {\n |\n }\n ]\n}');
|
|
938
|
+
const result = await source(cc);
|
|
939
|
+
const l = labels(result);
|
|
940
|
+
expect(l).toContain("name");
|
|
941
|
+
expect(l).toContain("valueString");
|
|
942
|
+
expect(l).not.toContain("parameter");
|
|
943
|
+
});
|
|
944
|
+
it("offers slice names for second array item name value", async ()=>{
|
|
945
|
+
const { cc } = completionAt('{\n "resourceType": "AidboxTopicDestination",\n "meta": {\n "profile": ["http://aidbox.app/StructureDefinition/aidboxtopicdestination-kafka-best-effort"]\n },\n "parameter": [\n {"name": "kafkaTopic", "valueString": "1"},\n {\n "name": "|\n }\n ]\n}');
|
|
946
|
+
const result = await source(cc);
|
|
947
|
+
const l = labels(result);
|
|
948
|
+
expect(l).toContain("bootstrapServers");
|
|
949
|
+
expect(l).toContain("batchSize");
|
|
950
|
+
});
|
|
951
|
+
it("offers part fields via contentReference", async ()=>{
|
|
952
|
+
const { cc } = completionAt('{\n "resourceType": "Parameters",\n "parameter": [\n {\n "name": "result",\n "part": [\n {\n |\n }\n ]\n }\n ]\n}');
|
|
953
|
+
const result = await source(cc);
|
|
954
|
+
const l = labels(result);
|
|
955
|
+
expect(l).toContain("name");
|
|
956
|
+
expect(l).toContain("valueString");
|
|
957
|
+
expect(l).toContain("part");
|
|
958
|
+
});
|
|
959
|
+
it("offers typed snippet for profile with value[x] constraint", async ()=>{
|
|
960
|
+
const typedSource = jsonCompletionSource(mockGetSDs, undefined, mockExpandValueSet);
|
|
961
|
+
const { cc } = completionAt('{\n "resourceType": "Parameters",\n "meta": {\n "profile": ["http://example.com/StructureDefinition/typed-params"]\n },\n "parameter": [\n |\n ]\n}');
|
|
962
|
+
const result = await typedSource(cc);
|
|
963
|
+
const l = labels(result);
|
|
964
|
+
expect(l).toContain("count");
|
|
965
|
+
expect(l).toContain("label");
|
|
966
|
+
});
|
|
967
|
+
it("works for Parameters-derived types (AidboxTopicDestination)", async ()=>{
|
|
968
|
+
const { cc } = completionAt('{\n "resourceType": "AidboxTopicDestination",\n "meta": {\n "profile": ["http://aidbox.app/StructureDefinition/aidboxtopicdestination-kafka-best-effort"]\n },\n "parameter": [\n |\n ]\n}');
|
|
969
|
+
const result = await source(cc);
|
|
970
|
+
const l = labels(result);
|
|
971
|
+
expect(l).toContain("kafkaTopic");
|
|
972
|
+
expect(l).toContain("bootstrapServers");
|
|
973
|
+
expect(l).toContain("batchSize");
|
|
974
|
+
});
|
|
975
|
+
it("offers slice names for derived type parameter.name", async ()=>{
|
|
976
|
+
const { cc } = completionAt('{\n "resourceType": "AidboxTopicDestination",\n "meta": {\n "profile": ["http://aidbox.app/StructureDefinition/aidboxtopicdestination-kafka-best-effort"]\n },\n "parameter": [\n {\n "name": "|\n }\n ]\n}');
|
|
977
|
+
const result = await source(cc);
|
|
978
|
+
const l = labels(result);
|
|
979
|
+
expect(l).toContain("kafkaTopic");
|
|
980
|
+
expect(l).toContain("bootstrapServers");
|
|
981
|
+
expect(l).toContain("batchSize");
|
|
982
|
+
});
|
|
983
|
+
it("offers fixed value for profiled field", async ()=>{
|
|
984
|
+
const { cc } = completionAt('{\n "resourceType": "AidboxTopicDestination",\n "meta": {\n "profile": ["http://aidbox.app/StructureDefinition/aidboxtopicdestination-kafka-best-effort"]\n },\n "kind": "|\n}');
|
|
985
|
+
const result = await source(cc);
|
|
986
|
+
const l = labels(result);
|
|
987
|
+
expect(l).toContain("kafka-best-effort");
|
|
988
|
+
});
|
|
989
|
+
it("generic parameter snippet is offered with lowest boost", async ()=>{
|
|
990
|
+
const doc = '{\n "resourceType": "Parameters",\n "parameter": [\n |\n ]\n}';
|
|
991
|
+
const { cc } = completionAt(doc);
|
|
992
|
+
const result = await source(cc);
|
|
993
|
+
const option = result?.options.find((o)=>o.label === "parameter");
|
|
994
|
+
expect(option).toBeDefined();
|
|
995
|
+
expect(option?.boost).toBe(-1);
|
|
996
|
+
expect(option?.info).toBe("Custom parameter");
|
|
997
|
+
});
|
|
998
|
+
});
|
|
999
|
+
describe("HTTP mode", ()=>{
|
|
1000
|
+
it("offers Patient fields in HTTP mode body", async ()=>{
|
|
1001
|
+
const doc = 'POST /fhir/Patient\nContent-Type: application/json\n\n{\n "resourceType": "Patient",\n |\n}';
|
|
1002
|
+
const { cc } = completionAt(doc);
|
|
1003
|
+
const result = await source(cc);
|
|
1004
|
+
const l = labels(result);
|
|
1005
|
+
expect(l).toContain("name");
|
|
1006
|
+
expect(l).toContain("gender");
|
|
1007
|
+
});
|
|
1008
|
+
it("offers gender codes in HTTP mode body", async ()=>{
|
|
1009
|
+
const doc = 'PUT /fhir/Patient/1\n\n{\n "resourceType": "Patient",\n "gender": "|\n}';
|
|
1010
|
+
const { cc } = completionAt(doc);
|
|
1011
|
+
const result = await source(cc);
|
|
1012
|
+
const l = labels(result);
|
|
1013
|
+
expect(l).toContain("male");
|
|
1014
|
+
expect(l).toContain("female");
|
|
1015
|
+
});
|
|
1016
|
+
it("offers Observation fields inside Bundle entry.resource via hint", async ()=>{
|
|
1017
|
+
const hintSource = jsonCompletionSource(mockGetSDs, "Bundle", mockExpandValueSet);
|
|
1018
|
+
const doc = 'POST /fhir/Bundle\nContent-Type: application/json\n\n{\n "entry": [\n {\n "resource": {\n "resourceType": "Observation",\n |\n }\n }\n ]\n}';
|
|
1019
|
+
const { cc } = completionAt(doc);
|
|
1020
|
+
const result = await hintSource(cc);
|
|
1021
|
+
const l = labels(result);
|
|
1022
|
+
expect(l).toContain("status");
|
|
1023
|
+
expect(l).toContain("code");
|
|
1024
|
+
expect(l).toContain("subject");
|
|
1025
|
+
expect(l).not.toContain("type");
|
|
1026
|
+
});
|
|
1027
|
+
});
|
|
1028
|
+
});
|
|
1029
|
+
describe("buildParameterSnippet", ()=>{
|
|
1030
|
+
it("inserts valueString by default when no value types", ()=>{
|
|
1031
|
+
const { text, cursorOffset } = buildParameterSnippet("package", [], " ");
|
|
1032
|
+
expect(text).toContain('"name": "package"');
|
|
1033
|
+
expect(text).toContain('"valueString": ""');
|
|
1034
|
+
// Cursor should be inside the empty valueString quotes
|
|
1035
|
+
expect(text[cursorOffset - 1]).toBe('"');
|
|
1036
|
+
expect(text[cursorOffset]).toBe('"');
|
|
1037
|
+
});
|
|
1038
|
+
it("inserts valueString for string-constrained type", ()=>{
|
|
1039
|
+
const { text } = buildParameterSnippet("label", [
|
|
1040
|
+
"string"
|
|
1041
|
+
], " ");
|
|
1042
|
+
expect(text).toContain('"name": "label"');
|
|
1043
|
+
expect(text).toContain('"valueString": ""');
|
|
1044
|
+
});
|
|
1045
|
+
it("inserts valueCode for code-constrained type", ()=>{
|
|
1046
|
+
const { text } = buildParameterSnippet("status", [
|
|
1047
|
+
"code"
|
|
1048
|
+
], " ");
|
|
1049
|
+
expect(text).toContain('"name": "status"');
|
|
1050
|
+
expect(text).toContain('"valueCode": ""');
|
|
1051
|
+
});
|
|
1052
|
+
it("inserts valueInteger for integer-constrained type", ()=>{
|
|
1053
|
+
const { text, cursorOffset } = buildParameterSnippet("count", [
|
|
1054
|
+
"integer"
|
|
1055
|
+
], " ");
|
|
1056
|
+
expect(text).toContain('"name": "count"');
|
|
1057
|
+
expect(text).toContain('"valueInteger": ');
|
|
1058
|
+
expect(text).not.toContain('"valueString"');
|
|
1059
|
+
// Cursor should be after ": "
|
|
1060
|
+
expect(text.slice(cursorOffset - 2, cursorOffset)).toBe(": ");
|
|
1061
|
+
});
|
|
1062
|
+
it("inserts valueBoolean for boolean-constrained type", ()=>{
|
|
1063
|
+
const { text } = buildParameterSnippet("active", [
|
|
1064
|
+
"boolean"
|
|
1065
|
+
], " ");
|
|
1066
|
+
expect(text).toContain('"name": "active"');
|
|
1067
|
+
expect(text).toContain('"valueBoolean": ');
|
|
1068
|
+
});
|
|
1069
|
+
it("inserts complex object for CodeableConcept type", ()=>{
|
|
1070
|
+
const { text } = buildParameterSnippet("code", [
|
|
1071
|
+
"CodeableConcept"
|
|
1072
|
+
], " ");
|
|
1073
|
+
expect(text).toContain('"name": "code"');
|
|
1074
|
+
expect(text).toContain('"valueCodeableConcept": {');
|
|
1075
|
+
expect(text).toContain("}");
|
|
1076
|
+
});
|
|
1077
|
+
it("uses correct indentation", ()=>{
|
|
1078
|
+
const { text } = buildParameterSnippet("test", [], " ");
|
|
1079
|
+
const lines = text.split("\n");
|
|
1080
|
+
// Line 0: {
|
|
1081
|
+
expect(lines[0]).toBe("{");
|
|
1082
|
+
// Line 1: inner indent + "name"
|
|
1083
|
+
expect(lines[1]).toMatch(/^ {4}"name": "test",$/);
|
|
1084
|
+
// Line 2: inner indent + "valueString"
|
|
1085
|
+
expect(lines[2]).toMatch(/^ {4}"valueString": ""$/);
|
|
1086
|
+
// Line 3: outer indent + }
|
|
1087
|
+
expect(lines[3]).toBe(" }");
|
|
1088
|
+
});
|
|
1089
|
+
it("inserts empty name for generic template with cursor in name", ()=>{
|
|
1090
|
+
const { text, cursorOffset } = buildParameterSnippet("", [], " ");
|
|
1091
|
+
expect(text).toContain('"name": ""');
|
|
1092
|
+
expect(text).toContain('"valueString": ""');
|
|
1093
|
+
// Cursor should be inside the name quotes (first ""), not valueString
|
|
1094
|
+
const nameQuoteIdx = text.indexOf('"name": ""') + '"name": "'.length;
|
|
1095
|
+
expect(cursorOffset).toBe(nameQuoteIdx);
|
|
1096
|
+
});
|
|
1097
|
+
});
|
|
1098
|
+
|
|
1099
|
+
//# sourceMappingURL=fhir-autocomplete.test.js.map
|