@fhir-dsl/fhirpath 0.6.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/LICENSE +21 -0
- package/dist/index.cjs +1133 -0
- package/dist/index.d.cts +542 -0
- package/dist/index.d.ts +542 -0
- package/dist/index.js +1102 -0
- package/package.json +51 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
PREDICATE_SYMBOL: () => PREDICATE_SYMBOL,
|
|
24
|
+
createPredicateProxy: () => createPredicateProxy,
|
|
25
|
+
evaluate: () => evaluate,
|
|
26
|
+
extractPredicate: () => extractPredicate,
|
|
27
|
+
fhirpath: () => fhirpath
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(index_exports);
|
|
30
|
+
|
|
31
|
+
// src/eval/combining.ts
|
|
32
|
+
function evalCombining(op, collection, ctx) {
|
|
33
|
+
const otherCollection = ctx.evaluateSub(op.other.ops, ctx.rootResource);
|
|
34
|
+
switch (op.type) {
|
|
35
|
+
case "union": {
|
|
36
|
+
const result = [...collection];
|
|
37
|
+
for (const item of otherCollection) {
|
|
38
|
+
if (!result.some((existing) => deepEqual(existing, item))) {
|
|
39
|
+
result.push(item);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
case "combine":
|
|
45
|
+
return [...collection, ...otherCollection];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function deepEqual(a, b) {
|
|
49
|
+
if (a === b) return true;
|
|
50
|
+
if (a == null || b == null) return false;
|
|
51
|
+
if (typeof a !== typeof b) return false;
|
|
52
|
+
if (typeof a !== "object") return false;
|
|
53
|
+
const aObj = a;
|
|
54
|
+
const bObj = b;
|
|
55
|
+
const aKeys = Object.keys(aObj);
|
|
56
|
+
const bKeys = Object.keys(bObj);
|
|
57
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
58
|
+
return aKeys.every((key) => deepEqual(aObj[key], bObj[key]));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/eval/conversion.ts
|
|
62
|
+
function evalConversion(op, collection) {
|
|
63
|
+
switch (op.type) {
|
|
64
|
+
case "toBoolean":
|
|
65
|
+
return collection.flatMap((item) => {
|
|
66
|
+
const result = convertToBoolean(item);
|
|
67
|
+
return result != null ? [result] : [];
|
|
68
|
+
});
|
|
69
|
+
case "toInteger":
|
|
70
|
+
return collection.flatMap((item) => {
|
|
71
|
+
const result = convertToInteger(item);
|
|
72
|
+
return result != null ? [result] : [];
|
|
73
|
+
});
|
|
74
|
+
case "toDecimal":
|
|
75
|
+
return collection.flatMap((item) => {
|
|
76
|
+
const result = convertToDecimal(item);
|
|
77
|
+
return result != null ? [result] : [];
|
|
78
|
+
});
|
|
79
|
+
case "toFhirString":
|
|
80
|
+
return collection.flatMap((item) => {
|
|
81
|
+
if (item == null) return [];
|
|
82
|
+
if (typeof item === "string") return [item];
|
|
83
|
+
if (typeof item === "number" || typeof item === "boolean") return [String(item)];
|
|
84
|
+
return [];
|
|
85
|
+
});
|
|
86
|
+
case "toDate":
|
|
87
|
+
return collection.flatMap((item) => {
|
|
88
|
+
if (typeof item !== "string") return [];
|
|
89
|
+
const match = /^\d{4}(-\d{2}(-\d{2})?)?/.exec(item);
|
|
90
|
+
return match ? [match[0]] : [];
|
|
91
|
+
});
|
|
92
|
+
case "toDateTime":
|
|
93
|
+
return collection.flatMap((item) => {
|
|
94
|
+
if (typeof item !== "string") return [];
|
|
95
|
+
const d = Date.parse(item);
|
|
96
|
+
return Number.isNaN(d) ? [] : [item];
|
|
97
|
+
});
|
|
98
|
+
case "toTime":
|
|
99
|
+
return collection.flatMap((item) => {
|
|
100
|
+
if (typeof item !== "string") return [];
|
|
101
|
+
const match = /^T?(\d{2}:\d{2}(:\d{2}(\.\d+)?)?)/.exec(item);
|
|
102
|
+
return match ? [match[1]] : [];
|
|
103
|
+
});
|
|
104
|
+
case "toQuantity":
|
|
105
|
+
return collection.flatMap((item) => {
|
|
106
|
+
if (typeof item === "number") {
|
|
107
|
+
return [{ value: item, unit: op.unit ?? "1" }];
|
|
108
|
+
}
|
|
109
|
+
if (typeof item === "object" && item != null && "value" in item) {
|
|
110
|
+
return [item];
|
|
111
|
+
}
|
|
112
|
+
return [];
|
|
113
|
+
});
|
|
114
|
+
case "convertsToBoolean":
|
|
115
|
+
return collection.flatMap((item) => [convertToBoolean(item) != null]);
|
|
116
|
+
case "convertsToInteger":
|
|
117
|
+
return collection.flatMap((item) => [convertToInteger(item) != null]);
|
|
118
|
+
case "convertsToDecimal":
|
|
119
|
+
return collection.flatMap((item) => [convertToDecimal(item) != null]);
|
|
120
|
+
case "convertsToString":
|
|
121
|
+
return collection.flatMap((item) => [
|
|
122
|
+
item != null && (typeof item === "string" || typeof item === "number" || typeof item === "boolean")
|
|
123
|
+
]);
|
|
124
|
+
case "convertsToDate":
|
|
125
|
+
return collection.flatMap((item) => {
|
|
126
|
+
if (typeof item !== "string") return [false];
|
|
127
|
+
return [/^\d{4}(-\d{2}(-\d{2})?)?/.test(item)];
|
|
128
|
+
});
|
|
129
|
+
case "convertsToDateTime":
|
|
130
|
+
return collection.flatMap((item) => {
|
|
131
|
+
if (typeof item !== "string") return [false];
|
|
132
|
+
return [!Number.isNaN(Date.parse(item))];
|
|
133
|
+
});
|
|
134
|
+
case "convertsToTime":
|
|
135
|
+
return collection.flatMap((item) => {
|
|
136
|
+
if (typeof item !== "string") return [false];
|
|
137
|
+
return [/^T?\d{2}:\d{2}(:\d{2}(\.\d+)?)?/.test(item)];
|
|
138
|
+
});
|
|
139
|
+
case "convertsToQuantity":
|
|
140
|
+
return collection.flatMap((item) => {
|
|
141
|
+
if (typeof item === "number") return [true];
|
|
142
|
+
if (typeof item === "object" && item != null && "value" in item) return [true];
|
|
143
|
+
return [false];
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function convertToBoolean(item) {
|
|
148
|
+
if (typeof item === "boolean") return item;
|
|
149
|
+
if (typeof item === "string") {
|
|
150
|
+
if (item === "true") return true;
|
|
151
|
+
if (item === "false") return false;
|
|
152
|
+
return void 0;
|
|
153
|
+
}
|
|
154
|
+
if (typeof item === "number") {
|
|
155
|
+
if (item === 1) return true;
|
|
156
|
+
if (item === 0) return false;
|
|
157
|
+
return void 0;
|
|
158
|
+
}
|
|
159
|
+
return void 0;
|
|
160
|
+
}
|
|
161
|
+
function convertToInteger(item) {
|
|
162
|
+
if (typeof item === "number" && Number.isInteger(item)) return item;
|
|
163
|
+
if (typeof item === "string") {
|
|
164
|
+
const n = Number.parseInt(item, 10);
|
|
165
|
+
return Number.isNaN(n) ? void 0 : n;
|
|
166
|
+
}
|
|
167
|
+
if (typeof item === "boolean") return item ? 1 : 0;
|
|
168
|
+
return void 0;
|
|
169
|
+
}
|
|
170
|
+
function convertToDecimal(item) {
|
|
171
|
+
if (typeof item === "number") return item;
|
|
172
|
+
if (typeof item === "string") {
|
|
173
|
+
const n = Number.parseFloat(item);
|
|
174
|
+
return Number.isNaN(n) ? void 0 : n;
|
|
175
|
+
}
|
|
176
|
+
if (typeof item === "boolean") return item ? 1 : 0;
|
|
177
|
+
return void 0;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/eval/existence.ts
|
|
181
|
+
function evalExistence(op, collection, ctx) {
|
|
182
|
+
switch (op.type) {
|
|
183
|
+
case "exists":
|
|
184
|
+
return [collection.length > 0];
|
|
185
|
+
case "exists_predicate":
|
|
186
|
+
return [collection.some((item) => isTruthy(ctx.evaluateSub(op.predicate.ops, item)))];
|
|
187
|
+
case "all":
|
|
188
|
+
return [collection.every((item) => isTruthy(ctx.evaluateSub(op.predicate.ops, item)))];
|
|
189
|
+
case "allTrue":
|
|
190
|
+
return [collection.length > 0 && collection.every((item) => item === true)];
|
|
191
|
+
case "anyTrue":
|
|
192
|
+
return [collection.some((item) => item === true)];
|
|
193
|
+
case "allFalse":
|
|
194
|
+
return [collection.length > 0 && collection.every((item) => item === false)];
|
|
195
|
+
case "anyFalse":
|
|
196
|
+
return [collection.some((item) => item === false)];
|
|
197
|
+
case "count":
|
|
198
|
+
return [collection.length];
|
|
199
|
+
case "empty":
|
|
200
|
+
return [collection.length === 0];
|
|
201
|
+
case "distinct": {
|
|
202
|
+
const seen = [];
|
|
203
|
+
return collection.filter((item) => {
|
|
204
|
+
if (seen.some((s) => deepEqual2(s, item))) return false;
|
|
205
|
+
seen.push(item);
|
|
206
|
+
return true;
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
case "isDistinct": {
|
|
210
|
+
const seen = [];
|
|
211
|
+
for (const item of collection) {
|
|
212
|
+
if (seen.some((s) => deepEqual2(s, item))) return [false];
|
|
213
|
+
seen.push(item);
|
|
214
|
+
}
|
|
215
|
+
return [true];
|
|
216
|
+
}
|
|
217
|
+
case "subsetOf": {
|
|
218
|
+
const otherCollection = ctx.evaluateSub(op.other.ops, ctx.rootResource);
|
|
219
|
+
return [collection.every((item) => otherCollection.some((o) => deepEqual2(item, o)))];
|
|
220
|
+
}
|
|
221
|
+
case "supersetOf": {
|
|
222
|
+
const otherCollection = ctx.evaluateSub(op.other.ops, ctx.rootResource);
|
|
223
|
+
return [otherCollection.every((item) => collection.some((c) => deepEqual2(c, item)))];
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function isTruthy(result) {
|
|
228
|
+
return result.length === 1 && result[0] === true;
|
|
229
|
+
}
|
|
230
|
+
function deepEqual2(a, b) {
|
|
231
|
+
if (a === b) return true;
|
|
232
|
+
if (a == null || b == null) return false;
|
|
233
|
+
if (typeof a !== typeof b) return false;
|
|
234
|
+
if (typeof a !== "object") return false;
|
|
235
|
+
const aObj = a;
|
|
236
|
+
const bObj = b;
|
|
237
|
+
const aKeys = Object.keys(aObj);
|
|
238
|
+
const bKeys = Object.keys(bObj);
|
|
239
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
240
|
+
return aKeys.every((key) => deepEqual2(aObj[key], bObj[key]));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/eval/filtering.ts
|
|
244
|
+
var TYPE_CHECKS = {
|
|
245
|
+
Quantity: ["value", "unit"],
|
|
246
|
+
CodeableConcept: ["coding"],
|
|
247
|
+
Coding: ["system", "code"],
|
|
248
|
+
HumanName: ["family", "given"],
|
|
249
|
+
Address: ["city", "line"],
|
|
250
|
+
ContactPoint: ["system", "value"],
|
|
251
|
+
Identifier: ["system", "value"],
|
|
252
|
+
Reference: ["reference"],
|
|
253
|
+
Period: ["start", "end"],
|
|
254
|
+
Range: ["low", "high"],
|
|
255
|
+
Ratio: ["numerator", "denominator"],
|
|
256
|
+
Attachment: ["contentType"],
|
|
257
|
+
Annotation: ["text"],
|
|
258
|
+
Money: ["value", "currency"]
|
|
259
|
+
};
|
|
260
|
+
function evalFiltering(op, collection, ctx) {
|
|
261
|
+
switch (op.type) {
|
|
262
|
+
case "where_simple":
|
|
263
|
+
return collection.filter(
|
|
264
|
+
(item) => item != null && typeof item === "object" && item[op.field] === op.value
|
|
265
|
+
);
|
|
266
|
+
case "where":
|
|
267
|
+
return collection.filter((item) => {
|
|
268
|
+
const result = ctx.evaluateSub(op.predicate.ops, item);
|
|
269
|
+
return result.length === 1 && result[0] === true;
|
|
270
|
+
});
|
|
271
|
+
case "select":
|
|
272
|
+
return collection.flatMap((item) => ctx.evaluateSub(op.projection.ops, item));
|
|
273
|
+
case "repeat": {
|
|
274
|
+
const result = [];
|
|
275
|
+
const seen = /* @__PURE__ */ new Set();
|
|
276
|
+
let current = [...collection];
|
|
277
|
+
while (current.length > 0) {
|
|
278
|
+
const next = [];
|
|
279
|
+
for (const item of current) {
|
|
280
|
+
const projected = ctx.evaluateSub(op.projection.ops, item);
|
|
281
|
+
for (const p of projected) {
|
|
282
|
+
if (!seen.has(p)) {
|
|
283
|
+
seen.add(p);
|
|
284
|
+
result.push(p);
|
|
285
|
+
next.push(p);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
current = next;
|
|
290
|
+
}
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
case "ofType":
|
|
294
|
+
return collection.filter((item) => matchesType(item, op.typeName));
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function matchesType(item, typeName) {
|
|
298
|
+
if (item == null) return false;
|
|
299
|
+
switch (typeName) {
|
|
300
|
+
case "string":
|
|
301
|
+
case "String":
|
|
302
|
+
return typeof item === "string";
|
|
303
|
+
case "boolean":
|
|
304
|
+
case "Boolean":
|
|
305
|
+
return typeof item === "boolean";
|
|
306
|
+
case "integer":
|
|
307
|
+
case "Integer":
|
|
308
|
+
return typeof item === "number" && Number.isInteger(item);
|
|
309
|
+
case "decimal":
|
|
310
|
+
case "Decimal":
|
|
311
|
+
return typeof item === "number";
|
|
312
|
+
}
|
|
313
|
+
if (typeof item !== "object") return false;
|
|
314
|
+
const obj = item;
|
|
315
|
+
if ("resourceType" in obj && obj.resourceType === typeName) return true;
|
|
316
|
+
const checkKeys = TYPE_CHECKS[typeName];
|
|
317
|
+
if (checkKeys) {
|
|
318
|
+
return checkKeys.some((key) => key in obj);
|
|
319
|
+
}
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/eval/math.ts
|
|
324
|
+
function evalMath(op, collection) {
|
|
325
|
+
switch (op.type) {
|
|
326
|
+
case "abs":
|
|
327
|
+
return collection.flatMap((item) => typeof item === "number" ? [Math.abs(item)] : []);
|
|
328
|
+
case "ceiling":
|
|
329
|
+
return collection.flatMap((item) => typeof item === "number" ? [Math.ceil(item)] : []);
|
|
330
|
+
case "exp":
|
|
331
|
+
return collection.flatMap((item) => typeof item === "number" ? [Math.exp(item)] : []);
|
|
332
|
+
case "floor":
|
|
333
|
+
return collection.flatMap((item) => typeof item === "number" ? [Math.floor(item)] : []);
|
|
334
|
+
case "ln":
|
|
335
|
+
return collection.flatMap((item) => typeof item === "number" ? [Math.log(item)] : []);
|
|
336
|
+
case "log":
|
|
337
|
+
return collection.flatMap((item) => typeof item === "number" ? [Math.log(item) / Math.log(op.base)] : []);
|
|
338
|
+
case "power":
|
|
339
|
+
return collection.flatMap((item) => typeof item === "number" ? [item ** op.exponent] : []);
|
|
340
|
+
case "round":
|
|
341
|
+
return collection.flatMap((item) => {
|
|
342
|
+
if (typeof item !== "number") return [];
|
|
343
|
+
const factor = 10 ** (op.precision ?? 0);
|
|
344
|
+
return [Math.round(item * factor) / factor];
|
|
345
|
+
});
|
|
346
|
+
case "sqrt":
|
|
347
|
+
return collection.flatMap((item) => typeof item === "number" ? [Math.sqrt(item)] : []);
|
|
348
|
+
case "truncate":
|
|
349
|
+
return collection.flatMap((item) => typeof item === "number" ? [Math.trunc(item)] : []);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// src/eval/nav.ts
|
|
354
|
+
function evalNav(op, collection) {
|
|
355
|
+
switch (op.type) {
|
|
356
|
+
case "nav":
|
|
357
|
+
return collection.flatMap((item) => {
|
|
358
|
+
if (item == null || typeof item !== "object") return [];
|
|
359
|
+
const val = item[op.prop];
|
|
360
|
+
if (val == null) return [];
|
|
361
|
+
return Array.isArray(val) ? val : [val];
|
|
362
|
+
});
|
|
363
|
+
case "children":
|
|
364
|
+
return collection.flatMap((item) => {
|
|
365
|
+
if (item == null || typeof item !== "object") return [];
|
|
366
|
+
return Object.values(item).flatMap((val) => {
|
|
367
|
+
if (val == null) return [];
|
|
368
|
+
return Array.isArray(val) ? val : [val];
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
case "descendants": {
|
|
372
|
+
const result = [];
|
|
373
|
+
const stack = [...collection];
|
|
374
|
+
while (stack.length > 0) {
|
|
375
|
+
const item = stack.pop();
|
|
376
|
+
if (item == null || typeof item !== "object") continue;
|
|
377
|
+
const children = Object.values(item).flatMap((val) => {
|
|
378
|
+
if (val == null) return [];
|
|
379
|
+
return Array.isArray(val) ? val : [val];
|
|
380
|
+
});
|
|
381
|
+
result.push(...children);
|
|
382
|
+
stack.push(...children.filter((c) => c != null && typeof c === "object"));
|
|
383
|
+
}
|
|
384
|
+
return result;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// src/eval/operators.ts
|
|
390
|
+
function evalOperator(op, collection, ctx) {
|
|
391
|
+
switch (op.type) {
|
|
392
|
+
case "eq":
|
|
393
|
+
return evalComparison(collection, op.value, ctx, (a, b) => a === b);
|
|
394
|
+
case "neq":
|
|
395
|
+
return evalComparison(collection, op.value, ctx, (a, b) => a !== b);
|
|
396
|
+
case "lt":
|
|
397
|
+
return evalComparison(collection, op.value, ctx, (a, b) => a < b);
|
|
398
|
+
case "gt":
|
|
399
|
+
return evalComparison(collection, op.value, ctx, (a, b) => a > b);
|
|
400
|
+
case "lte":
|
|
401
|
+
return evalComparison(collection, op.value, ctx, (a, b) => a <= b);
|
|
402
|
+
case "gte":
|
|
403
|
+
return evalComparison(collection, op.value, ctx, (a, b) => a >= b);
|
|
404
|
+
case "and": {
|
|
405
|
+
const left = toSingletonBoolean(collection);
|
|
406
|
+
if (left === false) return [false];
|
|
407
|
+
const right = toSingletonBoolean(ctx.evaluateSub(op.other.ops, ctx.rootResource));
|
|
408
|
+
if (left === true && right === true) return [true];
|
|
409
|
+
if (right === false) return [false];
|
|
410
|
+
return [];
|
|
411
|
+
}
|
|
412
|
+
case "or": {
|
|
413
|
+
const left = toSingletonBoolean(collection);
|
|
414
|
+
if (left === true) return [true];
|
|
415
|
+
const right = toSingletonBoolean(ctx.evaluateSub(op.other.ops, ctx.rootResource));
|
|
416
|
+
if (right === true) return [true];
|
|
417
|
+
if (left === false && right === false) return [false];
|
|
418
|
+
return [];
|
|
419
|
+
}
|
|
420
|
+
case "xor": {
|
|
421
|
+
const left = toSingletonBoolean(collection);
|
|
422
|
+
const right = toSingletonBoolean(ctx.evaluateSub(op.other.ops, ctx.rootResource));
|
|
423
|
+
if (left == null || right == null) return [];
|
|
424
|
+
return [left !== right];
|
|
425
|
+
}
|
|
426
|
+
case "not": {
|
|
427
|
+
const val = toSingletonBoolean(collection);
|
|
428
|
+
if (val == null) return [];
|
|
429
|
+
return [!val];
|
|
430
|
+
}
|
|
431
|
+
case "implies": {
|
|
432
|
+
const left = toSingletonBoolean(collection);
|
|
433
|
+
if (left === false) return [true];
|
|
434
|
+
const right = toSingletonBoolean(ctx.evaluateSub(op.other.ops, ctx.rootResource));
|
|
435
|
+
if (left === true && right === true) return [true];
|
|
436
|
+
if (right === true) return [true];
|
|
437
|
+
if (left === true && right === false) return [false];
|
|
438
|
+
return [];
|
|
439
|
+
}
|
|
440
|
+
case "is":
|
|
441
|
+
return [collection.length === 1 && matchesType2(collection[0], op.typeName)];
|
|
442
|
+
case "as":
|
|
443
|
+
return collection.filter((item) => matchesType2(item, op.typeName));
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
function evalComparison(collection, operand, ctx, comparator) {
|
|
447
|
+
if (collection.length === 0) return [];
|
|
448
|
+
const left = collection.length === 1 ? collection[0] : collection[0];
|
|
449
|
+
let right;
|
|
450
|
+
if (isCompiledPredicate(operand)) {
|
|
451
|
+
const rightCollection = ctx.evaluateSub(operand.ops, ctx.rootResource);
|
|
452
|
+
if (rightCollection.length === 0) return [];
|
|
453
|
+
right = rightCollection[0];
|
|
454
|
+
} else {
|
|
455
|
+
right = operand;
|
|
456
|
+
}
|
|
457
|
+
return [comparator(left, right)];
|
|
458
|
+
}
|
|
459
|
+
function isCompiledPredicate(value) {
|
|
460
|
+
return value != null && typeof value === "object" && "ops" in value && "compiledPath" in value;
|
|
461
|
+
}
|
|
462
|
+
function toSingletonBoolean(collection) {
|
|
463
|
+
if (collection.length !== 1) return void 0;
|
|
464
|
+
const val = collection[0];
|
|
465
|
+
if (typeof val === "boolean") return val;
|
|
466
|
+
return void 0;
|
|
467
|
+
}
|
|
468
|
+
function matchesType2(item, typeName) {
|
|
469
|
+
if (item == null) return false;
|
|
470
|
+
switch (typeName) {
|
|
471
|
+
case "string":
|
|
472
|
+
case "String":
|
|
473
|
+
return typeof item === "string";
|
|
474
|
+
case "boolean":
|
|
475
|
+
case "Boolean":
|
|
476
|
+
return typeof item === "boolean";
|
|
477
|
+
case "integer":
|
|
478
|
+
case "Integer":
|
|
479
|
+
return typeof item === "number" && Number.isInteger(item);
|
|
480
|
+
case "decimal":
|
|
481
|
+
case "Decimal":
|
|
482
|
+
return typeof item === "number";
|
|
483
|
+
}
|
|
484
|
+
if (typeof item !== "object") return false;
|
|
485
|
+
if ("resourceType" in item) {
|
|
486
|
+
return item.resourceType === typeName;
|
|
487
|
+
}
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/eval/strings.ts
|
|
492
|
+
function evalString(op, collection) {
|
|
493
|
+
switch (op.type) {
|
|
494
|
+
case "indexOf":
|
|
495
|
+
return collection.flatMap((item) => {
|
|
496
|
+
if (typeof item !== "string") return [];
|
|
497
|
+
return [item.indexOf(op.substring)];
|
|
498
|
+
});
|
|
499
|
+
case "substring":
|
|
500
|
+
return collection.flatMap((item) => {
|
|
501
|
+
if (typeof item !== "string") return [];
|
|
502
|
+
if (op.start < 0 || op.start >= item.length) return [];
|
|
503
|
+
const result = op.length != null ? item.substring(op.start, op.start + op.length) : item.substring(op.start);
|
|
504
|
+
return [result];
|
|
505
|
+
});
|
|
506
|
+
case "startsWith":
|
|
507
|
+
return collection.flatMap((item) => {
|
|
508
|
+
if (typeof item !== "string") return [];
|
|
509
|
+
return [item.startsWith(op.prefix)];
|
|
510
|
+
});
|
|
511
|
+
case "endsWith":
|
|
512
|
+
return collection.flatMap((item) => {
|
|
513
|
+
if (typeof item !== "string") return [];
|
|
514
|
+
return [item.endsWith(op.suffix)];
|
|
515
|
+
});
|
|
516
|
+
case "str_contains":
|
|
517
|
+
return collection.flatMap((item) => {
|
|
518
|
+
if (typeof item !== "string") return [];
|
|
519
|
+
return [item.includes(op.substring)];
|
|
520
|
+
});
|
|
521
|
+
case "upper":
|
|
522
|
+
return collection.flatMap((item) => {
|
|
523
|
+
if (typeof item !== "string") return [];
|
|
524
|
+
return [item.toUpperCase()];
|
|
525
|
+
});
|
|
526
|
+
case "lower":
|
|
527
|
+
return collection.flatMap((item) => {
|
|
528
|
+
if (typeof item !== "string") return [];
|
|
529
|
+
return [item.toLowerCase()];
|
|
530
|
+
});
|
|
531
|
+
case "replace":
|
|
532
|
+
return collection.flatMap((item) => {
|
|
533
|
+
if (typeof item !== "string") return [];
|
|
534
|
+
return [item.split(op.pattern).join(op.substitution)];
|
|
535
|
+
});
|
|
536
|
+
case "matches":
|
|
537
|
+
return collection.flatMap((item) => {
|
|
538
|
+
if (typeof item !== "string") return [];
|
|
539
|
+
return [new RegExp(op.regex).test(item)];
|
|
540
|
+
});
|
|
541
|
+
case "replaceMatches":
|
|
542
|
+
return collection.flatMap((item) => {
|
|
543
|
+
if (typeof item !== "string") return [];
|
|
544
|
+
return [item.replace(new RegExp(op.regex, "g"), op.substitution)];
|
|
545
|
+
});
|
|
546
|
+
case "str_length":
|
|
547
|
+
return collection.flatMap((item) => {
|
|
548
|
+
if (typeof item !== "string") return [];
|
|
549
|
+
return [item.length];
|
|
550
|
+
});
|
|
551
|
+
case "toChars":
|
|
552
|
+
return collection.flatMap((item) => {
|
|
553
|
+
if (typeof item !== "string") return [];
|
|
554
|
+
return item.split("");
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// src/eval/subsetting.ts
|
|
560
|
+
function evalSubsetting(op, collection, _ctx) {
|
|
561
|
+
switch (op.type) {
|
|
562
|
+
case "first":
|
|
563
|
+
return collection.length > 0 ? [collection[0]] : [];
|
|
564
|
+
case "last":
|
|
565
|
+
return collection.length > 0 ? [collection[collection.length - 1]] : [];
|
|
566
|
+
case "single":
|
|
567
|
+
if (collection.length > 1) {
|
|
568
|
+
throw new Error(`single() expected at most one element, got ${collection.length}`);
|
|
569
|
+
}
|
|
570
|
+
return collection.length === 1 ? [collection[0]] : [];
|
|
571
|
+
case "tail":
|
|
572
|
+
return collection.slice(1);
|
|
573
|
+
case "skip":
|
|
574
|
+
return collection.slice(op.num);
|
|
575
|
+
case "take":
|
|
576
|
+
return collection.slice(0, op.num);
|
|
577
|
+
case "intersect": {
|
|
578
|
+
const otherCollection = _ctx.evaluateSub(op.other.ops, _ctx.rootResource);
|
|
579
|
+
return collection.filter((item) => otherCollection.some((other) => deepEqual3(item, other)));
|
|
580
|
+
}
|
|
581
|
+
case "exclude": {
|
|
582
|
+
const otherCollection = _ctx.evaluateSub(op.other.ops, _ctx.rootResource);
|
|
583
|
+
return collection.filter((item) => !otherCollection.some((other) => deepEqual3(item, other)));
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
function deepEqual3(a, b) {
|
|
588
|
+
if (a === b) return true;
|
|
589
|
+
if (a == null || b == null) return false;
|
|
590
|
+
if (typeof a !== typeof b) return false;
|
|
591
|
+
if (typeof a !== "object") return false;
|
|
592
|
+
const aObj = a;
|
|
593
|
+
const bObj = b;
|
|
594
|
+
const aKeys = Object.keys(aObj);
|
|
595
|
+
const bKeys = Object.keys(bObj);
|
|
596
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
597
|
+
return aKeys.every((key) => deepEqual3(aObj[key], bObj[key]));
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// src/eval/utility.ts
|
|
601
|
+
function evalUtility(op, collection, ctx) {
|
|
602
|
+
switch (op.type) {
|
|
603
|
+
case "trace":
|
|
604
|
+
console.debug(`[FHIRPath trace] ${op.name}:`, collection);
|
|
605
|
+
return collection;
|
|
606
|
+
case "now":
|
|
607
|
+
return [(/* @__PURE__ */ new Date()).toISOString()];
|
|
608
|
+
case "timeOfDay": {
|
|
609
|
+
const now = /* @__PURE__ */ new Date();
|
|
610
|
+
return [
|
|
611
|
+
`${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}:${String(now.getSeconds()).padStart(2, "0")}`
|
|
612
|
+
];
|
|
613
|
+
}
|
|
614
|
+
case "today":
|
|
615
|
+
return [(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)];
|
|
616
|
+
case "iif": {
|
|
617
|
+
const criterionResult = collection.length > 0 ? ctx.evaluateSub(op.criterion.ops, collection[0]) : [];
|
|
618
|
+
const isTrue = criterionResult.length === 1 && criterionResult[0] === true;
|
|
619
|
+
if (isTrue) {
|
|
620
|
+
return collection.flatMap((item) => ctx.evaluateSub(op.trueResult.ops, item));
|
|
621
|
+
}
|
|
622
|
+
if (op.otherwiseResult) {
|
|
623
|
+
return collection.flatMap((item) => ctx.evaluateSub(op.otherwiseResult.ops, item));
|
|
624
|
+
}
|
|
625
|
+
return [];
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// src/evaluator.ts
|
|
631
|
+
function evaluate(ops, resource) {
|
|
632
|
+
let collection = [resource];
|
|
633
|
+
const ctx = {
|
|
634
|
+
rootResource: resource,
|
|
635
|
+
evaluateSub: evaluate
|
|
636
|
+
};
|
|
637
|
+
for (const op of ops) {
|
|
638
|
+
collection = dispatch(op, collection, ctx);
|
|
639
|
+
}
|
|
640
|
+
return collection;
|
|
641
|
+
}
|
|
642
|
+
function dispatch(op, collection, ctx) {
|
|
643
|
+
switch (op.type) {
|
|
644
|
+
// --- Navigation ---
|
|
645
|
+
case "nav":
|
|
646
|
+
case "children":
|
|
647
|
+
case "descendants":
|
|
648
|
+
return evalNav(op, collection);
|
|
649
|
+
// --- Filtering ---
|
|
650
|
+
case "where":
|
|
651
|
+
case "where_simple":
|
|
652
|
+
case "select":
|
|
653
|
+
case "repeat":
|
|
654
|
+
case "ofType":
|
|
655
|
+
return evalFiltering(op, collection, ctx);
|
|
656
|
+
// --- Existence ---
|
|
657
|
+
case "exists":
|
|
658
|
+
case "exists_predicate":
|
|
659
|
+
case "all":
|
|
660
|
+
case "allTrue":
|
|
661
|
+
case "anyTrue":
|
|
662
|
+
case "allFalse":
|
|
663
|
+
case "anyFalse":
|
|
664
|
+
case "count":
|
|
665
|
+
case "empty":
|
|
666
|
+
case "distinct":
|
|
667
|
+
case "isDistinct":
|
|
668
|
+
case "subsetOf":
|
|
669
|
+
case "supersetOf":
|
|
670
|
+
return evalExistence(op, collection, ctx);
|
|
671
|
+
// --- Subsetting ---
|
|
672
|
+
case "first":
|
|
673
|
+
case "last":
|
|
674
|
+
case "single":
|
|
675
|
+
case "tail":
|
|
676
|
+
case "skip":
|
|
677
|
+
case "take":
|
|
678
|
+
case "intersect":
|
|
679
|
+
case "exclude":
|
|
680
|
+
return evalSubsetting(op, collection, ctx);
|
|
681
|
+
// --- Combining ---
|
|
682
|
+
case "union":
|
|
683
|
+
case "combine":
|
|
684
|
+
return evalCombining(op, collection, ctx);
|
|
685
|
+
// --- String ---
|
|
686
|
+
case "indexOf":
|
|
687
|
+
case "substring":
|
|
688
|
+
case "startsWith":
|
|
689
|
+
case "endsWith":
|
|
690
|
+
case "str_contains":
|
|
691
|
+
case "upper":
|
|
692
|
+
case "lower":
|
|
693
|
+
case "replace":
|
|
694
|
+
case "matches":
|
|
695
|
+
case "replaceMatches":
|
|
696
|
+
case "str_length":
|
|
697
|
+
case "toChars":
|
|
698
|
+
return evalString(op, collection);
|
|
699
|
+
// --- Math ---
|
|
700
|
+
case "abs":
|
|
701
|
+
case "ceiling":
|
|
702
|
+
case "exp":
|
|
703
|
+
case "floor":
|
|
704
|
+
case "ln":
|
|
705
|
+
case "log":
|
|
706
|
+
case "power":
|
|
707
|
+
case "round":
|
|
708
|
+
case "sqrt":
|
|
709
|
+
case "truncate":
|
|
710
|
+
return evalMath(op, collection);
|
|
711
|
+
// --- Conversion ---
|
|
712
|
+
case "toBoolean":
|
|
713
|
+
case "toInteger":
|
|
714
|
+
case "toDecimal":
|
|
715
|
+
case "toFhirString":
|
|
716
|
+
case "toDate":
|
|
717
|
+
case "toDateTime":
|
|
718
|
+
case "toTime":
|
|
719
|
+
case "toQuantity":
|
|
720
|
+
case "convertsToBoolean":
|
|
721
|
+
case "convertsToInteger":
|
|
722
|
+
case "convertsToDecimal":
|
|
723
|
+
case "convertsToString":
|
|
724
|
+
case "convertsToDate":
|
|
725
|
+
case "convertsToDateTime":
|
|
726
|
+
case "convertsToTime":
|
|
727
|
+
case "convertsToQuantity":
|
|
728
|
+
return evalConversion(op, collection);
|
|
729
|
+
// --- Utility ---
|
|
730
|
+
case "trace":
|
|
731
|
+
case "now":
|
|
732
|
+
case "timeOfDay":
|
|
733
|
+
case "today":
|
|
734
|
+
case "iif":
|
|
735
|
+
return evalUtility(op, collection, ctx);
|
|
736
|
+
// --- Operators ---
|
|
737
|
+
case "eq":
|
|
738
|
+
case "neq":
|
|
739
|
+
case "lt":
|
|
740
|
+
case "gt":
|
|
741
|
+
case "lte":
|
|
742
|
+
case "gte":
|
|
743
|
+
case "and":
|
|
744
|
+
case "or":
|
|
745
|
+
case "xor":
|
|
746
|
+
case "not":
|
|
747
|
+
case "implies":
|
|
748
|
+
case "is":
|
|
749
|
+
case "as":
|
|
750
|
+
return evalOperator(op, collection, ctx);
|
|
751
|
+
// --- Literal ---
|
|
752
|
+
case "literal":
|
|
753
|
+
return [op.value];
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// src/expression.ts
|
|
758
|
+
var PREDICATE_SYMBOL = /* @__PURE__ */ Symbol.for("fhirpath.predicate");
|
|
759
|
+
function extractPredicate(proxy) {
|
|
760
|
+
if (proxy != null && typeof proxy === "object" && PREDICATE_SYMBOL in proxy) {
|
|
761
|
+
return proxy[PREDICATE_SYMBOL];
|
|
762
|
+
}
|
|
763
|
+
throw new Error("Expected a FHIRPath predicate expression");
|
|
764
|
+
}
|
|
765
|
+
function createPredicateProxy(path, ops) {
|
|
766
|
+
const predicate = { ops, compiledPath: path };
|
|
767
|
+
return new Proxy({}, {
|
|
768
|
+
has(_, prop) {
|
|
769
|
+
if (prop === PREDICATE_SYMBOL) return true;
|
|
770
|
+
return false;
|
|
771
|
+
},
|
|
772
|
+
get(_, prop) {
|
|
773
|
+
if (prop === PREDICATE_SYMBOL) return predicate;
|
|
774
|
+
if (typeof prop === "symbol") {
|
|
775
|
+
if (prop === Symbol.toPrimitive || prop === Symbol.toStringTag) return () => path;
|
|
776
|
+
return void 0;
|
|
777
|
+
}
|
|
778
|
+
if (prop === "then") return void 0;
|
|
779
|
+
if (prop === "toJSON" || prop === "toString") return () => path;
|
|
780
|
+
if (prop === "eq") {
|
|
781
|
+
return (value) => createPredicateProxy(`${path} = ${formatValue(value)}`, [...ops, { type: "eq", value: resolveValue(value) }]);
|
|
782
|
+
}
|
|
783
|
+
if (prop === "neq") {
|
|
784
|
+
return (value) => createPredicateProxy(`${path} != ${formatValue(value)}`, [
|
|
785
|
+
...ops,
|
|
786
|
+
{ type: "neq", value: resolveValue(value) }
|
|
787
|
+
]);
|
|
788
|
+
}
|
|
789
|
+
if (prop === "lt") {
|
|
790
|
+
return (value) => createPredicateProxy(`${path} < ${formatValue(value)}`, [...ops, { type: "lt", value: resolveValue(value) }]);
|
|
791
|
+
}
|
|
792
|
+
if (prop === "gt") {
|
|
793
|
+
return (value) => createPredicateProxy(`${path} > ${formatValue(value)}`, [...ops, { type: "gt", value: resolveValue(value) }]);
|
|
794
|
+
}
|
|
795
|
+
if (prop === "lte") {
|
|
796
|
+
return (value) => createPredicateProxy(`${path} <= ${formatValue(value)}`, [
|
|
797
|
+
...ops,
|
|
798
|
+
{ type: "lte", value: resolveValue(value) }
|
|
799
|
+
]);
|
|
800
|
+
}
|
|
801
|
+
if (prop === "gte") {
|
|
802
|
+
return (value) => createPredicateProxy(`${path} >= ${formatValue(value)}`, [
|
|
803
|
+
...ops,
|
|
804
|
+
{ type: "gte", value: resolveValue(value) }
|
|
805
|
+
]);
|
|
806
|
+
}
|
|
807
|
+
if (prop === "and") {
|
|
808
|
+
return (other) => {
|
|
809
|
+
const otherPred = extractPredicate(other);
|
|
810
|
+
return createPredicateProxy(`${path} and ${otherPred.compiledPath}`, [
|
|
811
|
+
...ops,
|
|
812
|
+
{ type: "and", other: otherPred }
|
|
813
|
+
]);
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
if (prop === "or") {
|
|
817
|
+
return (other) => {
|
|
818
|
+
const otherPred = extractPredicate(other);
|
|
819
|
+
return createPredicateProxy(`${path} or ${otherPred.compiledPath}`, [
|
|
820
|
+
...ops,
|
|
821
|
+
{ type: "or", other: otherPred }
|
|
822
|
+
]);
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
if (prop === "xor") {
|
|
826
|
+
return (other) => {
|
|
827
|
+
const otherPred = extractPredicate(other);
|
|
828
|
+
return createPredicateProxy(`${path} xor ${otherPred.compiledPath}`, [
|
|
829
|
+
...ops,
|
|
830
|
+
{ type: "xor", other: otherPred }
|
|
831
|
+
]);
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
if (prop === "not") {
|
|
835
|
+
return () => createPredicateProxy(`${path}.not()`, [...ops, { type: "not" }]);
|
|
836
|
+
}
|
|
837
|
+
if (prop === "implies") {
|
|
838
|
+
return (other) => {
|
|
839
|
+
const otherPred = extractPredicate(other);
|
|
840
|
+
return createPredicateProxy(`${path} implies ${otherPred.compiledPath}`, [
|
|
841
|
+
...ops,
|
|
842
|
+
{ type: "implies", other: otherPred }
|
|
843
|
+
]);
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
if (prop === "contains") {
|
|
847
|
+
return (substring) => createPredicateProxy(`${path}.contains('${substring}')`, [...ops, { type: "str_contains", substring }]);
|
|
848
|
+
}
|
|
849
|
+
if (prop === "startsWith") {
|
|
850
|
+
return (prefix) => createPredicateProxy(`${path}.startsWith('${prefix}')`, [...ops, { type: "startsWith", prefix }]);
|
|
851
|
+
}
|
|
852
|
+
if (prop === "endsWith") {
|
|
853
|
+
return (suffix) => createPredicateProxy(`${path}.endsWith('${suffix}')`, [...ops, { type: "endsWith", suffix }]);
|
|
854
|
+
}
|
|
855
|
+
if (prop === "matches") {
|
|
856
|
+
return (regex) => createPredicateProxy(`${path}.matches('${regex}')`, [...ops, { type: "matches", regex }]);
|
|
857
|
+
}
|
|
858
|
+
if (prop === "exists") {
|
|
859
|
+
return () => createPredicateProxy(`${path}.exists()`, [...ops, { type: "exists" }]);
|
|
860
|
+
}
|
|
861
|
+
if (prop === "empty") {
|
|
862
|
+
return () => createPredicateProxy(`${path}.empty()`, [...ops, { type: "empty" }]);
|
|
863
|
+
}
|
|
864
|
+
if (prop === "count") {
|
|
865
|
+
return () => createPredicateProxy(`${path}.count()`, [...ops, { type: "count" }]);
|
|
866
|
+
}
|
|
867
|
+
if (prop === "is") {
|
|
868
|
+
return (typeName) => createPredicateProxy(`${path} is ${typeName}`, [...ops, { type: "is", typeName }]);
|
|
869
|
+
}
|
|
870
|
+
if (prop === "as") {
|
|
871
|
+
return (typeName) => createPredicateProxy(`${path} as ${typeName}`, [...ops, { type: "as", typeName }]);
|
|
872
|
+
}
|
|
873
|
+
return createPredicateProxy(`${path}.${prop}`, [...ops, { type: "nav", prop }]);
|
|
874
|
+
}
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
function resolveValue(value) {
|
|
878
|
+
if (value != null && typeof value === "object" && PREDICATE_SYMBOL in value) {
|
|
879
|
+
return extractPredicate(value);
|
|
880
|
+
}
|
|
881
|
+
return value;
|
|
882
|
+
}
|
|
883
|
+
function formatValue(value) {
|
|
884
|
+
if (value != null && typeof value === "object" && PREDICATE_SYMBOL in value) {
|
|
885
|
+
return extractPredicate(value).compiledPath;
|
|
886
|
+
}
|
|
887
|
+
if (typeof value === "string") return `'${value}'`;
|
|
888
|
+
return String(value);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// src/builder.ts
|
|
892
|
+
var NULLARY_FNS = {
|
|
893
|
+
first: { opType: "first", compile: (p) => `${p}.first()` },
|
|
894
|
+
last: { opType: "last", compile: (p) => `${p}.last()` },
|
|
895
|
+
single: { opType: "single", compile: (p) => `${p}.single()` },
|
|
896
|
+
tail: { opType: "tail", compile: (p) => `${p}.tail()` },
|
|
897
|
+
distinct: { opType: "distinct", compile: (p) => `${p}.distinct()` },
|
|
898
|
+
isDistinct: { opType: "isDistinct", compile: (p) => `${p}.isDistinct()` },
|
|
899
|
+
allTrue: { opType: "allTrue", compile: (p) => `${p}.allTrue()` },
|
|
900
|
+
anyTrue: { opType: "anyTrue", compile: (p) => `${p}.anyTrue()` },
|
|
901
|
+
allFalse: { opType: "allFalse", compile: (p) => `${p}.allFalse()` },
|
|
902
|
+
anyFalse: { opType: "anyFalse", compile: (p) => `${p}.anyFalse()` },
|
|
903
|
+
upper: { opType: "upper", compile: (p) => `${p}.upper()` },
|
|
904
|
+
lower: { opType: "lower", compile: (p) => `${p}.lower()` },
|
|
905
|
+
toChars: { opType: "toChars", compile: (p) => `${p}.toChars()` },
|
|
906
|
+
abs: { opType: "abs", compile: (p) => `${p}.abs()` },
|
|
907
|
+
ceiling: { opType: "ceiling", compile: (p) => `${p}.ceiling()` },
|
|
908
|
+
exp: { opType: "exp", compile: (p) => `${p}.exp()` },
|
|
909
|
+
floor: { opType: "floor", compile: (p) => `${p}.floor()` },
|
|
910
|
+
ln: { opType: "ln", compile: (p) => `${p}.ln()` },
|
|
911
|
+
sqrt: { opType: "sqrt", compile: (p) => `${p}.sqrt()` },
|
|
912
|
+
truncate: { opType: "truncate", compile: (p) => `${p}.truncate()` },
|
|
913
|
+
toBoolean: { opType: "toBoolean", compile: (p) => `${p}.toBoolean()` },
|
|
914
|
+
toInteger: { opType: "toInteger", compile: (p) => `${p}.toInteger()` },
|
|
915
|
+
toDecimal: { opType: "toDecimal", compile: (p) => `${p}.toDecimal()` },
|
|
916
|
+
toDate: { opType: "toDate", compile: (p) => `${p}.toDate()` },
|
|
917
|
+
toDateTime: { opType: "toDateTime", compile: (p) => `${p}.toDateTime()` },
|
|
918
|
+
toTime: { opType: "toTime", compile: (p) => `${p}.toTime()` },
|
|
919
|
+
convertsToBoolean: { opType: "convertsToBoolean", compile: (p) => `${p}.convertsToBoolean()` },
|
|
920
|
+
convertsToInteger: { opType: "convertsToInteger", compile: (p) => `${p}.convertsToInteger()` },
|
|
921
|
+
convertsToDecimal: { opType: "convertsToDecimal", compile: (p) => `${p}.convertsToDecimal()` },
|
|
922
|
+
convertsToString: { opType: "convertsToString", compile: (p) => `${p}.convertsToString()` },
|
|
923
|
+
convertsToDate: { opType: "convertsToDate", compile: (p) => `${p}.convertsToDate()` },
|
|
924
|
+
convertsToDateTime: { opType: "convertsToDateTime", compile: (p) => `${p}.convertsToDateTime()` },
|
|
925
|
+
convertsToTime: { opType: "convertsToTime", compile: (p) => `${p}.convertsToTime()` },
|
|
926
|
+
convertsToQuantity: { opType: "convertsToQuantity", compile: (p) => `${p}.convertsToQuantity()` },
|
|
927
|
+
not: { opType: "not", compile: (p) => `${p}.not()` },
|
|
928
|
+
children: { opType: "children", compile: (p) => `${p}.children()` },
|
|
929
|
+
descendants: { opType: "descendants", compile: (p) => `${p}.descendants()` }
|
|
930
|
+
};
|
|
931
|
+
function buildPredicate(callback) {
|
|
932
|
+
const $this = createPredicateProxy("$this", []);
|
|
933
|
+
const result = callback($this);
|
|
934
|
+
return extractPredicate(result);
|
|
935
|
+
}
|
|
936
|
+
function exprFromOther(other) {
|
|
937
|
+
if (other != null && typeof other === "object" && PREDICATE_SYMBOL in other) {
|
|
938
|
+
return extractPredicate(other);
|
|
939
|
+
}
|
|
940
|
+
const compiled = other.compile();
|
|
941
|
+
const ops = other[/* @__PURE__ */ Symbol.for("fhirpath.ops")];
|
|
942
|
+
return { ops: ops ?? [], compiledPath: compiled ?? "" };
|
|
943
|
+
}
|
|
944
|
+
function createExprProxy(path, ops) {
|
|
945
|
+
return new Proxy({}, {
|
|
946
|
+
get(_, prop) {
|
|
947
|
+
if (typeof prop === "symbol") {
|
|
948
|
+
if (prop === Symbol.toPrimitive || prop === Symbol.toStringTag) return () => path;
|
|
949
|
+
if (prop === /* @__PURE__ */ Symbol.for("fhirpath.ops")) return ops;
|
|
950
|
+
if (prop === PREDICATE_SYMBOL) return { ops, compiledPath: path };
|
|
951
|
+
return void 0;
|
|
952
|
+
}
|
|
953
|
+
if (prop === "then") return void 0;
|
|
954
|
+
if (prop === "toJSON" || prop === "toString") return () => path;
|
|
955
|
+
if (prop === "compile") return () => path;
|
|
956
|
+
if (prop === "evaluate") return (resource) => evaluate(ops, resource);
|
|
957
|
+
if (prop in NULLARY_FNS) {
|
|
958
|
+
const fn = NULLARY_FNS[prop];
|
|
959
|
+
return () => createExprProxy(fn.compile(path), [...ops, { type: fn.opType }]);
|
|
960
|
+
}
|
|
961
|
+
if (prop === "where") {
|
|
962
|
+
return (...args) => {
|
|
963
|
+
if (typeof args[0] === "function") {
|
|
964
|
+
const pred = buildPredicate(args[0]);
|
|
965
|
+
return createExprProxy(`${path}.where(${pred.compiledPath})`, [...ops, { type: "where", predicate: pred }]);
|
|
966
|
+
}
|
|
967
|
+
const field = args[0];
|
|
968
|
+
const value = args[1];
|
|
969
|
+
return createExprProxy(`${path}.where(${field} = '${value}')`, [
|
|
970
|
+
...ops,
|
|
971
|
+
{ type: "where_simple", field, value }
|
|
972
|
+
]);
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
if (prop === "exists") {
|
|
976
|
+
return (...args) => {
|
|
977
|
+
if (args.length > 0 && typeof args[0] === "function") {
|
|
978
|
+
const pred = buildPredicate(args[0]);
|
|
979
|
+
return createExprProxy(`${path}.exists(${pred.compiledPath})`, [
|
|
980
|
+
...ops,
|
|
981
|
+
{ type: "exists_predicate", predicate: pred }
|
|
982
|
+
]);
|
|
983
|
+
}
|
|
984
|
+
return createExprProxy(`${path}.exists()`, [...ops, { type: "exists" }]);
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
if (prop === "all") {
|
|
988
|
+
return (callback) => {
|
|
989
|
+
const pred = buildPredicate(callback);
|
|
990
|
+
return createExprProxy(`${path}.all(${pred.compiledPath})`, [...ops, { type: "all", predicate: pred }]);
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
if (prop === "select") {
|
|
994
|
+
return (callback) => {
|
|
995
|
+
const pred = buildPredicate(callback);
|
|
996
|
+
return createExprProxy(`${path}.select(${pred.compiledPath})`, [
|
|
997
|
+
...ops,
|
|
998
|
+
{ type: "select", projection: pred }
|
|
999
|
+
]);
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
if (prop === "repeat") {
|
|
1003
|
+
return (callback) => {
|
|
1004
|
+
const pred = buildPredicate(callback);
|
|
1005
|
+
return createExprProxy(`${path}.repeat(${pred.compiledPath})`, [
|
|
1006
|
+
...ops,
|
|
1007
|
+
{ type: "repeat", projection: pred }
|
|
1008
|
+
]);
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
if (prop === "count") return () => createExprProxy(`${path}.count()`, [...ops, { type: "count" }]);
|
|
1012
|
+
if (prop === "empty") return () => createExprProxy(`${path}.empty()`, [...ops, { type: "empty" }]);
|
|
1013
|
+
if (prop === "ofType") {
|
|
1014
|
+
return (typeName) => createExprProxy(`${path}.ofType(${typeName})`, [...ops, { type: "ofType", typeName }]);
|
|
1015
|
+
}
|
|
1016
|
+
if (prop === "union" || prop === "combine" || prop === "intersect" || prop === "exclude") {
|
|
1017
|
+
return (other) => {
|
|
1018
|
+
const otherPred = exprFromOther(other);
|
|
1019
|
+
return createExprProxy(`${path}.${prop}(${otherPred.compiledPath})`, [
|
|
1020
|
+
...ops,
|
|
1021
|
+
{ type: prop, other: otherPred }
|
|
1022
|
+
]);
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
if (prop === "subsetOf" || prop === "supersetOf") {
|
|
1026
|
+
return (other) => {
|
|
1027
|
+
const otherPred = exprFromOther(other);
|
|
1028
|
+
return createExprProxy(`${path}.${prop}(${otherPred.compiledPath})`, [
|
|
1029
|
+
...ops,
|
|
1030
|
+
{ type: prop, other: otherPred }
|
|
1031
|
+
]);
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
if (prop === "skip") {
|
|
1035
|
+
return (num) => createExprProxy(`${path}.skip(${num})`, [...ops, { type: "skip", num }]);
|
|
1036
|
+
}
|
|
1037
|
+
if (prop === "take") {
|
|
1038
|
+
return (num) => createExprProxy(`${path}.take(${num})`, [...ops, { type: "take", num }]);
|
|
1039
|
+
}
|
|
1040
|
+
if (prop === "indexOf") {
|
|
1041
|
+
return (substring) => createExprProxy(`${path}.indexOf('${substring}')`, [...ops, { type: "indexOf", substring }]);
|
|
1042
|
+
}
|
|
1043
|
+
if (prop === "substring") {
|
|
1044
|
+
return (start, length) => {
|
|
1045
|
+
const compiled = length != null ? `${path}.substring(${start}, ${length})` : `${path}.substring(${start})`;
|
|
1046
|
+
const op = length != null ? { type: "substring", start, length } : { type: "substring", start };
|
|
1047
|
+
return createExprProxy(compiled, [...ops, op]);
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
if (prop === "startsWith") {
|
|
1051
|
+
return (prefix) => createExprProxy(`${path}.startsWith('${prefix}')`, [...ops, { type: "startsWith", prefix }]);
|
|
1052
|
+
}
|
|
1053
|
+
if (prop === "endsWith") {
|
|
1054
|
+
return (suffix) => createExprProxy(`${path}.endsWith('${suffix}')`, [...ops, { type: "endsWith", suffix }]);
|
|
1055
|
+
}
|
|
1056
|
+
if (prop === "contains") {
|
|
1057
|
+
return (substring) => createExprProxy(`${path}.contains('${substring}')`, [...ops, { type: "str_contains", substring }]);
|
|
1058
|
+
}
|
|
1059
|
+
if (prop === "matches") {
|
|
1060
|
+
return (regex) => createExprProxy(`${path}.matches('${regex}')`, [...ops, { type: "matches", regex }]);
|
|
1061
|
+
}
|
|
1062
|
+
if (prop === "replace") {
|
|
1063
|
+
return (pattern, substitution) => createExprProxy(`${path}.replace('${pattern}', '${substitution}')`, [
|
|
1064
|
+
...ops,
|
|
1065
|
+
{ type: "replace", pattern, substitution }
|
|
1066
|
+
]);
|
|
1067
|
+
}
|
|
1068
|
+
if (prop === "replaceMatches") {
|
|
1069
|
+
return (regex, substitution) => createExprProxy(`${path}.replaceMatches('${regex}', '${substitution}')`, [
|
|
1070
|
+
...ops,
|
|
1071
|
+
{ type: "replaceMatches", regex, substitution }
|
|
1072
|
+
]);
|
|
1073
|
+
}
|
|
1074
|
+
if (prop === "length") {
|
|
1075
|
+
return () => createExprProxy(`${path}.length()`, [...ops, { type: "str_length" }]);
|
|
1076
|
+
}
|
|
1077
|
+
if (prop === "log") {
|
|
1078
|
+
return (base) => createExprProxy(`${path}.log(${base})`, [...ops, { type: "log", base }]);
|
|
1079
|
+
}
|
|
1080
|
+
if (prop === "power") {
|
|
1081
|
+
return (exponent) => createExprProxy(`${path}.power(${exponent})`, [...ops, { type: "power", exponent }]);
|
|
1082
|
+
}
|
|
1083
|
+
if (prop === "round") {
|
|
1084
|
+
return (precision) => {
|
|
1085
|
+
const compiled = precision != null ? `${path}.round(${precision})` : `${path}.round()`;
|
|
1086
|
+
const op = precision != null ? { type: "round", precision } : { type: "round" };
|
|
1087
|
+
return createExprProxy(compiled, [...ops, op]);
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
if (prop === "toQuantity") {
|
|
1091
|
+
return (unit) => {
|
|
1092
|
+
const compiled = unit != null ? `${path}.toQuantity('${unit}')` : `${path}.toQuantity()`;
|
|
1093
|
+
const op = unit != null ? { type: "toQuantity", unit } : { type: "toQuantity" };
|
|
1094
|
+
return createExprProxy(compiled, [...ops, op]);
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
if (prop === "toFhirString") {
|
|
1098
|
+
return () => createExprProxy(`${path}.toString()`, [...ops, { type: "toFhirString" }]);
|
|
1099
|
+
}
|
|
1100
|
+
if (prop === "trace") {
|
|
1101
|
+
return (name) => createExprProxy(`${path}.trace('${name}')`, [...ops, { type: "trace", name }]);
|
|
1102
|
+
}
|
|
1103
|
+
if (prop === "iif") {
|
|
1104
|
+
return (criterionCb, trueResultCb, otherwiseResultCb) => {
|
|
1105
|
+
const criterion = buildPredicate(criterionCb);
|
|
1106
|
+
const trueResult = buildPredicate(trueResultCb);
|
|
1107
|
+
const otherwiseResult = otherwiseResultCb ? buildPredicate(otherwiseResultCb) : void 0;
|
|
1108
|
+
const otherwisePart = otherwiseResult ? `, ${otherwiseResult.compiledPath}` : "";
|
|
1109
|
+
const op = otherwiseResult ? { type: "iif", criterion, trueResult, otherwiseResult } : { type: "iif", criterion, trueResult };
|
|
1110
|
+
return createExprProxy(`${path}.iif(${criterion.compiledPath}, ${trueResult.compiledPath}${otherwisePart})`, [
|
|
1111
|
+
...ops,
|
|
1112
|
+
op
|
|
1113
|
+
]);
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
if (prop === "now") return () => createExprProxy("now()", [{ type: "now" }]);
|
|
1117
|
+
if (prop === "timeOfDay") return () => createExprProxy("timeOfDay()", [{ type: "timeOfDay" }]);
|
|
1118
|
+
if (prop === "today") return () => createExprProxy("today()", [{ type: "today" }]);
|
|
1119
|
+
return createExprProxy(`${path}.${prop}`, [...ops, { type: "nav", prop }]);
|
|
1120
|
+
}
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
function fhirpath(resourceType) {
|
|
1124
|
+
return createExprProxy(resourceType, []);
|
|
1125
|
+
}
|
|
1126
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1127
|
+
0 && (module.exports = {
|
|
1128
|
+
PREDICATE_SYMBOL,
|
|
1129
|
+
createPredicateProxy,
|
|
1130
|
+
evaluate,
|
|
1131
|
+
extractPredicate,
|
|
1132
|
+
fhirpath
|
|
1133
|
+
});
|