@metriport/fhir-sdk 1.2.7 → 1.3.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/README.md +207 -0
- package/dist/__tests__/date-range-performance.test.d.ts +2 -0
- package/dist/__tests__/date-range-performance.test.d.ts.map +1 -0
- package/dist/__tests__/date-range-performance.test.js +218 -0
- package/dist/__tests__/date-range-performance.test.js.map +1 -0
- package/dist/__tests__/date-range-search.test.d.ts +2 -0
- package/dist/__tests__/date-range-search.test.d.ts.map +1 -0
- package/dist/__tests__/date-range-search.test.js +215 -0
- package/dist/__tests__/date-range-search.test.js.map +1 -0
- package/dist/__tests__/fhir-bundle-sdk.test.js +467 -0
- package/dist/__tests__/fhir-bundle-sdk.test.js.map +1 -1
- package/dist/__tests__/reverse-references.test.d.ts +2 -0
- package/dist/__tests__/reverse-references.test.d.ts.map +1 -0
- package/dist/__tests__/reverse-references.test.js +241 -0
- package/dist/__tests__/reverse-references.test.js.map +1 -0
- package/dist/demo-reverse-references.d.ts +7 -0
- package/dist/demo-reverse-references.d.ts.map +1 -0
- package/dist/demo-reverse-references.js +207 -0
- package/dist/demo-reverse-references.js.map +1 -0
- package/dist/fhir-bundle-sdk.d.ts +222 -0
- package/dist/fhir-bundle-sdk.d.ts.map +1 -0
- package/dist/fhir-bundle-sdk.js +527 -0
- package/dist/fhir-bundle-sdk.js.map +1 -0
- package/dist/index.d.ts +2 -242
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -677
- package/dist/index.js.map +1 -1
- package/dist/internal/bundle-operations.d.ts +14 -0
- package/dist/internal/bundle-operations.d.ts.map +1 -0
- package/dist/internal/bundle-operations.js +55 -0
- package/dist/internal/bundle-operations.js.map +1 -0
- package/dist/internal/date-extraction.d.ts +12 -0
- package/dist/internal/date-extraction.d.ts.map +1 -0
- package/dist/internal/date-extraction.js +206 -0
- package/dist/internal/date-extraction.js.map +1 -0
- package/dist/internal/graph-traversal.d.ts +13 -0
- package/dist/internal/graph-traversal.d.ts.map +1 -0
- package/dist/internal/graph-traversal.js +89 -0
- package/dist/internal/graph-traversal.js.map +1 -0
- package/dist/internal/indexing.d.ts +17 -0
- package/dist/internal/indexing.d.ts.map +1 -0
- package/dist/internal/indexing.js +118 -0
- package/dist/internal/indexing.js.map +1 -0
- package/dist/internal/llm-context.d.ts +39 -0
- package/dist/internal/llm-context.d.ts.map +1 -0
- package/dist/internal/llm-context.js +188 -0
- package/dist/internal/llm-context.js.map +1 -0
- package/dist/internal/reference-resolution.d.ts +25 -0
- package/dist/internal/reference-resolution.d.ts.map +1 -0
- package/dist/internal/reference-resolution.js +133 -0
- package/dist/internal/reference-resolution.js.map +1 -0
- package/dist/internal/reference-utils.d.ts +26 -0
- package/dist/internal/reference-utils.d.ts.map +1 -0
- package/dist/internal/reference-utils.js +89 -0
- package/dist/internal/reference-utils.js.map +1 -0
- package/dist/internal/validation.d.ts +16 -0
- package/dist/internal/validation.d.ts.map +1 -0
- package/dist/internal/validation.js +45 -0
- package/dist/internal/validation.js.map +1 -0
- package/dist/types/sdk-types.d.ts +107 -0
- package/dist/types/sdk-types.d.ts.map +1 -0
- package/dist/types/sdk-types.js +17 -0
- package/dist/types/sdk-types.js.map +1 -0
- package/dist/types/smart-resources.d.ts +97 -4
- package/dist/types/smart-resources.d.ts.map +1 -1
- package/dist/types/smart-resources.js +82 -5
- package/dist/types/smart-resources.js.map +1 -1
- package/dist/utils/interval-tree/index.d.ts +87 -0
- package/dist/utils/interval-tree/index.d.ts.map +1 -0
- package/dist/utils/interval-tree/index.js +774 -0
- package/dist/utils/interval-tree/index.js.map +1 -0
- package/dist/utils/interval-tree/shallowequal/arrays.d.ts +3 -0
- package/dist/utils/interval-tree/shallowequal/arrays.d.ts.map +1 -0
- package/dist/utils/interval-tree/shallowequal/arrays.js +25 -0
- package/dist/utils/interval-tree/shallowequal/arrays.js.map +1 -0
- package/dist/utils/interval-tree/shallowequal/index.d.ts +6 -0
- package/dist/utils/interval-tree/shallowequal/index.d.ts.map +1 -0
- package/dist/utils/interval-tree/shallowequal/index.js +28 -0
- package/dist/utils/interval-tree/shallowequal/index.js.map +1 -0
- package/dist/utils/interval-tree/shallowequal/objects.d.ts +3 -0
- package/dist/utils/interval-tree/shallowequal/objects.d.ts.map +1 -0
- package/dist/utils/interval-tree/shallowequal/objects.js +28 -0
- package/dist/utils/interval-tree/shallowequal/objects.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Resource } from "@medplum/fhirtypes";
|
|
2
|
+
import { Smart } from "../types/smart-resources";
|
|
3
|
+
import { LLMContextOptions, WalkResult } from "../types/sdk-types";
|
|
4
|
+
/**
|
|
5
|
+
* Strip non-clinical metadata from a FHIR resource to reduce noise for LLM consumption.
|
|
6
|
+
* Removes: meta, extension, modifierExtension, text
|
|
7
|
+
* Returns an immutable copy without mutating the original.
|
|
8
|
+
*/
|
|
9
|
+
export declare function stripNonClinicalData<T extends Resource>(resource: T): T;
|
|
10
|
+
/**
|
|
11
|
+
* Group resources by their resource type
|
|
12
|
+
*/
|
|
13
|
+
export declare function groupResourcesByType(resources: Resource[]): Record<string, Resource[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Format a single resource as readable text
|
|
16
|
+
*/
|
|
17
|
+
export declare function formatResourceAsText(resource: Resource): string;
|
|
18
|
+
/**
|
|
19
|
+
* Format resources as structured text for better LLM readability
|
|
20
|
+
*/
|
|
21
|
+
export declare function formatAsStructuredText(resourcesByDepth: Map<number, Resource[]>): string;
|
|
22
|
+
/**
|
|
23
|
+
* Format resources as JSON with depth annotations
|
|
24
|
+
*/
|
|
25
|
+
export declare function formatAsJSON(resourcesByDepth: Map<number, Resource[]>): string;
|
|
26
|
+
/**
|
|
27
|
+
* Generate LLM-friendly context from a starting resource and its related resources.
|
|
28
|
+
* Uses BFS to discover related resources, strips non-clinical data, and formats output.
|
|
29
|
+
*
|
|
30
|
+
* @param startResource - The smart resource to start traversal from
|
|
31
|
+
* @param options - Options for depth, inclusion, and format
|
|
32
|
+
* @param walkReferencesFn - Function to walk references (passed in to avoid circular dependency)
|
|
33
|
+
* @returns Formatted string suitable for LLM context
|
|
34
|
+
*/
|
|
35
|
+
export declare function generateLLMContext<T extends Resource>(startResource: Smart<T>, options: LLMContextOptions | undefined, walkReferencesFn: (startResource: Smart<T>, options: {
|
|
36
|
+
maxDepth: number;
|
|
37
|
+
includeStartResource: boolean;
|
|
38
|
+
}) => WalkResult<T>): string;
|
|
39
|
+
//# sourceMappingURL=llm-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-context.d.ts","sourceRoot":"","sources":["../../src/internal/llm-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEnE;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,QAAQ,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CA+BvE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAetF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CA0C/D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,MAAM,CAqCxF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,MAAM,CAc9E;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,QAAQ,EACnD,aAAa,EAAE,KAAK,CAAC,CAAC,CAAC,EACvB,OAAO,EAAE,iBAAiB,GAAG,SAAS,EACtC,gBAAgB,EAAE,CAChB,aAAa,EAAE,KAAK,CAAC,CAAC,CAAC,EACvB,OAAO,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,oBAAoB,EAAE,OAAO,CAAA;CAAE,KACzD,UAAU,CAAC,CAAC,CAAC,GACjB,MAAM,CAoCR"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateLLMContext = exports.formatAsJSON = exports.formatAsStructuredText = exports.formatResourceAsText = exports.groupResourcesByType = exports.stripNonClinicalData = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Strip non-clinical metadata from a FHIR resource to reduce noise for LLM consumption.
|
|
6
|
+
* Removes: meta, extension, modifierExtension, text
|
|
7
|
+
* Returns an immutable copy without mutating the original.
|
|
8
|
+
*/
|
|
9
|
+
function stripNonClinicalData(resource) {
|
|
10
|
+
function deepCloneAndStrip(obj) {
|
|
11
|
+
if (obj === null || obj === undefined) {
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
if (Array.isArray(obj)) {
|
|
15
|
+
return obj.map(item => deepCloneAndStrip(item));
|
|
16
|
+
}
|
|
17
|
+
if (typeof obj === "object") {
|
|
18
|
+
const result = {};
|
|
19
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
20
|
+
// Skip non-clinical fields
|
|
21
|
+
if (key === "meta" ||
|
|
22
|
+
key === "extension" ||
|
|
23
|
+
key === "modifierExtension" ||
|
|
24
|
+
key === "text") {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
result[key] = deepCloneAndStrip(value);
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
return obj;
|
|
32
|
+
}
|
|
33
|
+
return deepCloneAndStrip(resource);
|
|
34
|
+
}
|
|
35
|
+
exports.stripNonClinicalData = stripNonClinicalData;
|
|
36
|
+
/**
|
|
37
|
+
* Group resources by their resource type
|
|
38
|
+
*/
|
|
39
|
+
function groupResourcesByType(resources) {
|
|
40
|
+
const grouped = {};
|
|
41
|
+
for (const resource of resources) {
|
|
42
|
+
const type = resource.resourceType;
|
|
43
|
+
if (!grouped[type]) {
|
|
44
|
+
grouped[type] = [];
|
|
45
|
+
}
|
|
46
|
+
const resourceArray = grouped[type];
|
|
47
|
+
if (resourceArray) {
|
|
48
|
+
resourceArray.push(resource);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return grouped;
|
|
52
|
+
}
|
|
53
|
+
exports.groupResourcesByType = groupResourcesByType;
|
|
54
|
+
/**
|
|
55
|
+
* Format a single resource as readable text
|
|
56
|
+
*/
|
|
57
|
+
function formatResourceAsText(resource) {
|
|
58
|
+
const lines = [];
|
|
59
|
+
lines.push(`[Type: ${resource.resourceType}, ID: ${resource.id ?? "unknown"}]`);
|
|
60
|
+
function formatValue(value, indent = 2) {
|
|
61
|
+
const prefix = " ".repeat(indent);
|
|
62
|
+
const result = [];
|
|
63
|
+
if (value === null || value === undefined) {
|
|
64
|
+
return [`${prefix}(none)`];
|
|
65
|
+
}
|
|
66
|
+
if (Array.isArray(value)) {
|
|
67
|
+
if (value.length === 0) {
|
|
68
|
+
return [`${prefix}[]`];
|
|
69
|
+
}
|
|
70
|
+
for (const [index, item] of value.entries()) {
|
|
71
|
+
result.push(`${prefix}[${index}]:`);
|
|
72
|
+
result.push(...formatValue(item, indent + 2));
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
if (typeof value === "object") {
|
|
77
|
+
for (const [key, val] of Object.entries(value)) {
|
|
78
|
+
result.push(`${prefix}${key}:`);
|
|
79
|
+
result.push(...formatValue(val, indent + 2));
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
return [`${prefix}${String(value)}`];
|
|
84
|
+
}
|
|
85
|
+
// Format key fields
|
|
86
|
+
for (const [key, value] of Object.entries(resource)) {
|
|
87
|
+
if (key === "resourceType" || key === "id")
|
|
88
|
+
continue; // Already shown in header
|
|
89
|
+
lines.push(` ${key}:`);
|
|
90
|
+
lines.push(...formatValue(value, 4));
|
|
91
|
+
}
|
|
92
|
+
return lines.join("\n");
|
|
93
|
+
}
|
|
94
|
+
exports.formatResourceAsText = formatResourceAsText;
|
|
95
|
+
/**
|
|
96
|
+
* Format resources as structured text for better LLM readability
|
|
97
|
+
*/
|
|
98
|
+
function formatAsStructuredText(resourcesByDepth) {
|
|
99
|
+
const lines = [];
|
|
100
|
+
// Primary resource section
|
|
101
|
+
const primaryResources = resourcesByDepth.get(0) ?? [];
|
|
102
|
+
if (primaryResources.length > 0) {
|
|
103
|
+
lines.push("PRIMARY RESOURCE:");
|
|
104
|
+
lines.push("=".repeat(80));
|
|
105
|
+
for (const resource of primaryResources) {
|
|
106
|
+
lines.push(formatResourceAsText(resource));
|
|
107
|
+
lines.push("");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Related resources by depth
|
|
111
|
+
for (const [depth, resources] of resourcesByDepth.entries()) {
|
|
112
|
+
if (depth === 0)
|
|
113
|
+
continue; // Already handled above
|
|
114
|
+
const depthLabel = depth === 1 ? "DIRECTLY REFERENCED" : `INDIRECTLY REFERENCED (Depth ${depth})`;
|
|
115
|
+
lines.push(`${depthLabel} RESOURCES:`);
|
|
116
|
+
lines.push("=".repeat(80));
|
|
117
|
+
// Group by resource type for better organization
|
|
118
|
+
const groupedByType = groupResourcesByType(resources);
|
|
119
|
+
for (const [resourceType, typeResources] of Object.entries(groupedByType)) {
|
|
120
|
+
lines.push(`\n--- ${resourceType} (${typeResources.length}) ---\n`);
|
|
121
|
+
for (const resource of typeResources) {
|
|
122
|
+
lines.push(formatResourceAsText(resource));
|
|
123
|
+
lines.push("");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return lines.join("\n");
|
|
128
|
+
}
|
|
129
|
+
exports.formatAsStructuredText = formatAsStructuredText;
|
|
130
|
+
/**
|
|
131
|
+
* Format resources as JSON with depth annotations
|
|
132
|
+
*/
|
|
133
|
+
function formatAsJSON(resourcesByDepth) {
|
|
134
|
+
const relatedResourcesByDepth = {};
|
|
135
|
+
for (const [depth, resources] of resourcesByDepth.entries()) {
|
|
136
|
+
if (depth === 0)
|
|
137
|
+
continue; // Skip primary resource (already at top)
|
|
138
|
+
relatedResourcesByDepth[`depth_${depth}`] = resources;
|
|
139
|
+
}
|
|
140
|
+
const output = {
|
|
141
|
+
primaryResource: resourcesByDepth.get(0)?.[0],
|
|
142
|
+
relatedResourcesByDepth,
|
|
143
|
+
};
|
|
144
|
+
return JSON.stringify(output, null, 2);
|
|
145
|
+
}
|
|
146
|
+
exports.formatAsJSON = formatAsJSON;
|
|
147
|
+
/**
|
|
148
|
+
* Generate LLM-friendly context from a starting resource and its related resources.
|
|
149
|
+
* Uses BFS to discover related resources, strips non-clinical data, and formats output.
|
|
150
|
+
*
|
|
151
|
+
* @param startResource - The smart resource to start traversal from
|
|
152
|
+
* @param options - Options for depth, inclusion, and format
|
|
153
|
+
* @param walkReferencesFn - Function to walk references (passed in to avoid circular dependency)
|
|
154
|
+
* @returns Formatted string suitable for LLM context
|
|
155
|
+
*/
|
|
156
|
+
function generateLLMContext(startResource, options, walkReferencesFn) {
|
|
157
|
+
const maxDepth = options?.maxDepth ?? 2;
|
|
158
|
+
const includeStartResource = options?.includeStartResource ?? true;
|
|
159
|
+
const format = options?.format ?? "structured-text";
|
|
160
|
+
// Walk the reference graph
|
|
161
|
+
const walkResult = walkReferencesFn(startResource, {
|
|
162
|
+
maxDepth,
|
|
163
|
+
includeStartResource,
|
|
164
|
+
});
|
|
165
|
+
// Log resource counts for debugging
|
|
166
|
+
console.log(`[LLM Context] Total resources discovered: ${walkResult.allResources.length}`);
|
|
167
|
+
for (const [depth, resources] of walkResult.resourcesByDepth.entries()) {
|
|
168
|
+
console.log(`[LLM Context] Depth ${depth}: ${resources.length} resources`);
|
|
169
|
+
}
|
|
170
|
+
// Strip non-clinical data from all resources
|
|
171
|
+
const cleanedResourcesByDepth = new Map();
|
|
172
|
+
for (const [depth, resources] of walkResult.resourcesByDepth.entries()) {
|
|
173
|
+
// Skip depth 0 if includeStartResource is false
|
|
174
|
+
if (depth === 0 && !includeStartResource) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
cleanedResourcesByDepth.set(depth, resources.map(r => stripNonClinicalData(r)));
|
|
178
|
+
}
|
|
179
|
+
// Format based on selected format
|
|
180
|
+
if (format === "json") {
|
|
181
|
+
return formatAsJSON(cleanedResourcesByDepth);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
return formatAsStructuredText(cleanedResourcesByDepth);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
exports.generateLLMContext = generateLLMContext;
|
|
188
|
+
//# sourceMappingURL=llm-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-context.js","sourceRoot":"","sources":["../../src/internal/llm-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Resource } from "@medplum/fhirtypes";
|
|
2
|
+
import { Smart } from "../types/smart-resources";
|
|
3
|
+
import { ReverseReferenceOptions } from "../types/sdk-types";
|
|
4
|
+
/**
|
|
5
|
+
* Check if a reference field expects an array of references
|
|
6
|
+
*/
|
|
7
|
+
export declare function isArrayReferenceField(fieldName: string): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a single reference object to a resource
|
|
10
|
+
* FR-5.5: Reference resolution methods handle both resource.id and fullUrl matching
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveReferenceObject(referenceObj: unknown, resourcesById: Map<string, Resource>, resourcesByFullUrl: Map<string, Resource>, resolutionStack: Set<string>): Resource | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve a reference method call to actual resources
|
|
15
|
+
* FR-5.2-5.6: Handle different reference types and patterns
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveReference(methodName: string, resource: Resource, resourcesById: Map<string, Resource>, resourcesByFullUrl: Map<string, Resource>, resolutionStack: Set<string>, createSmartResourceFn: <T extends Resource>(resource: T) => Smart<T>): Smart<Resource> | Smart<Resource>[] | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Create a smart resource with reference resolution methods
|
|
20
|
+
* FR-5.1: Resources returned by SDK have additional getter methods for each Reference field
|
|
21
|
+
* FR-5.7: Reference resolution operates in O(1) time complexity per reference
|
|
22
|
+
* FR-5.8: Original reference fields remain unchanged
|
|
23
|
+
*/
|
|
24
|
+
export declare function createSmartResource<T extends Resource>(resource: T, smartResourceCache: WeakMap<Resource, Smart<Resource>>, resourcesById: Map<string, Resource>, resourcesByFullUrl: Map<string, Resource>, resolutionStack: Set<string>, getResourcesReferencingIdFn: (targetId: string, options?: ReverseReferenceOptions) => Smart<Resource>[]): Smart<T>;
|
|
25
|
+
//# sourceMappingURL=reference-resolution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reference-resolution.d.ts","sourceRoot":"","sources":["../../src/internal/reference-resolution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAwC,MAAM,0BAA0B,CAAC;AACvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE7D;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAIhE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,OAAO,EACrB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EACpC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EACzC,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,GAC3B,QAAQ,GAAG,SAAS,CAgCtB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EACpC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EACzC,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,EAC5B,qBAAqB,EAAE,CAAC,CAAC,SAAS,QAAQ,EAAE,QAAQ,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,GACnE,KAAK,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,EAAE,GAAG,SAAS,CA0CjD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,QAAQ,EACpD,QAAQ,EAAE,CAAC,EACX,kBAAkB,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,EACtD,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EACpC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EACzC,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,EAC5B,2BAA2B,EAAE,CAC3B,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,uBAAuB,KAC9B,KAAK,CAAC,QAAQ,CAAC,EAAE,GACrB,KAAK,CAAC,CAAC,CAAC,CA+DV"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSmartResource = exports.resolveReference = exports.resolveReferenceObject = exports.isArrayReferenceField = void 0;
|
|
4
|
+
const smart_resources_1 = require("../types/smart-resources");
|
|
5
|
+
/**
|
|
6
|
+
* Check if a reference field expects an array of references
|
|
7
|
+
*/
|
|
8
|
+
function isArrayReferenceField(fieldName) {
|
|
9
|
+
// Known array reference fields
|
|
10
|
+
const arrayFields = new Set(["performer", "participant", "result", "generalPractitioner"]);
|
|
11
|
+
return arrayFields.has(fieldName);
|
|
12
|
+
}
|
|
13
|
+
exports.isArrayReferenceField = isArrayReferenceField;
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a single reference object to a resource
|
|
16
|
+
* FR-5.5: Reference resolution methods handle both resource.id and fullUrl matching
|
|
17
|
+
*/
|
|
18
|
+
function resolveReferenceObject(referenceObj, resourcesById, resourcesByFullUrl, resolutionStack) {
|
|
19
|
+
if (!referenceObj || typeof referenceObj !== "object" || !("reference" in referenceObj)) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const reference = referenceObj.reference;
|
|
23
|
+
// Circular reference protection
|
|
24
|
+
if (resolutionStack.has(reference)) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
resolutionStack.add(reference);
|
|
28
|
+
try {
|
|
29
|
+
// Try to resolve by resource ID (e.g., "Patient/123")
|
|
30
|
+
if (reference.includes("/")) {
|
|
31
|
+
const [, resourceId] = reference.split("/");
|
|
32
|
+
if (resourceId && resourcesById.has(resourceId)) {
|
|
33
|
+
return resourcesById.get(resourceId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Try to resolve by fullUrl (e.g., "urn:uuid:123")
|
|
37
|
+
if (resourcesByFullUrl.has(reference)) {
|
|
38
|
+
return resourcesByFullUrl.get(reference);
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
resolutionStack.delete(reference);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.resolveReferenceObject = resolveReferenceObject;
|
|
47
|
+
/**
|
|
48
|
+
* Resolve a reference method call to actual resources
|
|
49
|
+
* FR-5.2-5.6: Handle different reference types and patterns
|
|
50
|
+
*/
|
|
51
|
+
function resolveReference(methodName, resource, resourcesById, resourcesByFullUrl, resolutionStack, createSmartResourceFn) {
|
|
52
|
+
const referenceField = (0, smart_resources_1.getReferenceField)(methodName, resource.resourceType);
|
|
53
|
+
if (!referenceField) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
|
+
const referenceValue = resource[referenceField];
|
|
58
|
+
if (!referenceValue) {
|
|
59
|
+
// FR-5.6: Return appropriate empty value for missing references
|
|
60
|
+
return isArrayReferenceField(referenceField) ? [] : undefined;
|
|
61
|
+
}
|
|
62
|
+
// Handle array references
|
|
63
|
+
if (Array.isArray(referenceValue)) {
|
|
64
|
+
const resolvedResources = [];
|
|
65
|
+
for (const ref of referenceValue) {
|
|
66
|
+
const resolved = resolveReferenceObject(ref, resourcesById, resourcesByFullUrl, resolutionStack);
|
|
67
|
+
if (resolved) {
|
|
68
|
+
resolvedResources.push(createSmartResourceFn(resolved));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return resolvedResources;
|
|
72
|
+
}
|
|
73
|
+
// Handle single reference - we know it's not an array at this point
|
|
74
|
+
const resolved = resolveReferenceObject(referenceValue, resourcesById, resourcesByFullUrl, resolutionStack);
|
|
75
|
+
if (resolved) {
|
|
76
|
+
// Type assertion is safe here because we've established this is the single reference path
|
|
77
|
+
return createSmartResourceFn(resolved);
|
|
78
|
+
}
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
exports.resolveReference = resolveReference;
|
|
82
|
+
/**
|
|
83
|
+
* Create a smart resource with reference resolution methods
|
|
84
|
+
* FR-5.1: Resources returned by SDK have additional getter methods for each Reference field
|
|
85
|
+
* FR-5.7: Reference resolution operates in O(1) time complexity per reference
|
|
86
|
+
* FR-5.8: Original reference fields remain unchanged
|
|
87
|
+
*/
|
|
88
|
+
function createSmartResource(resource, smartResourceCache, resourcesById, resourcesByFullUrl, resolutionStack, getResourcesReferencingIdFn) {
|
|
89
|
+
// Check cache first to maintain object identity
|
|
90
|
+
const cached = smartResourceCache.get(resource);
|
|
91
|
+
if (cached) {
|
|
92
|
+
return cached;
|
|
93
|
+
}
|
|
94
|
+
const smartResource = new Proxy(resource, {
|
|
95
|
+
get: (target, prop, receiver) => {
|
|
96
|
+
// Handle the smart resource marker
|
|
97
|
+
if (prop === "__isSmartResource") {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
// Handle reverse reference method
|
|
101
|
+
if (prop === "getReferencingResources") {
|
|
102
|
+
return (options) => {
|
|
103
|
+
const resourceId = target.id;
|
|
104
|
+
if (!resourceId) {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
return getResourcesReferencingIdFn(resourceId, options);
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
// Check if this is a reference method call
|
|
111
|
+
if (typeof prop === "string" && (0, smart_resources_1.isReferenceMethod)(prop, target.resourceType)) {
|
|
112
|
+
return () => resolveReference(prop, target, resourcesById, resourcesByFullUrl, resolutionStack, res => createSmartResource(res, smartResourceCache, resourcesById, resourcesByFullUrl, resolutionStack, getResourcesReferencingIdFn));
|
|
113
|
+
}
|
|
114
|
+
// Return original property
|
|
115
|
+
return Reflect.get(target, prop, receiver);
|
|
116
|
+
},
|
|
117
|
+
// Ensure JSON serialization works correctly (FR-5.8)
|
|
118
|
+
ownKeys: target => {
|
|
119
|
+
return Reflect.ownKeys(target).filter(key => key !== "__isSmartResource" && key !== "getReferencingResources");
|
|
120
|
+
},
|
|
121
|
+
getOwnPropertyDescriptor: (target, prop) => {
|
|
122
|
+
if (prop === "__isSmartResource" || prop === "getReferencingResources") {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
return Reflect.getOwnPropertyDescriptor(target, prop);
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
// Cache the smart resource
|
|
129
|
+
smartResourceCache.set(resource, smartResource);
|
|
130
|
+
return smartResource;
|
|
131
|
+
}
|
|
132
|
+
exports.createSmartResource = createSmartResource;
|
|
133
|
+
//# sourceMappingURL=reference-resolution.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reference-resolution.js","sourceRoot":"","sources":["../../src/internal/reference-resolution.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Resource } from "@medplum/fhirtypes";
|
|
2
|
+
/**
|
|
3
|
+
* Find all Reference fields in a resource recursively
|
|
4
|
+
*/
|
|
5
|
+
export declare function findAllReferences(resource: Resource): Array<{
|
|
6
|
+
field: string;
|
|
7
|
+
reference: string;
|
|
8
|
+
}>;
|
|
9
|
+
/**
|
|
10
|
+
* Extract target resource ID from a reference string
|
|
11
|
+
*/
|
|
12
|
+
export declare function extractTargetIdFromReference(reference: string): string | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Normalize reference field path to base field name
|
|
15
|
+
* e.g., "participant.individual" -> "participant"
|
|
16
|
+
* "performer[0]" -> "performer"
|
|
17
|
+
*/
|
|
18
|
+
export declare function normalizeReferenceField(field: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Check if a reference can be resolved within the bundle
|
|
21
|
+
* @param reference - The reference string to check
|
|
22
|
+
* @param resourcesById - Map of resources indexed by resource.id
|
|
23
|
+
* @param resourcesByFullUrl - Map of resources indexed by entry.fullUrl
|
|
24
|
+
*/
|
|
25
|
+
export declare function canResolveReference(reference: string, resourcesById: Map<string, Resource>, resourcesByFullUrl: Map<string, Resource>): boolean;
|
|
26
|
+
//# sourceMappingURL=reference-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reference-utils.d.ts","sourceRoot":"","sources":["../../src/internal/reference-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAkCjG;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAalF;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM7D;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EACpC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,GACxC,OAAO,CAeT"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.canResolveReference = exports.normalizeReferenceField = exports.extractTargetIdFromReference = exports.findAllReferences = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Find all Reference fields in a resource recursively
|
|
6
|
+
*/
|
|
7
|
+
function findAllReferences(resource) {
|
|
8
|
+
const references = [];
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
function searchObject(obj, path = "") {
|
|
11
|
+
if (!obj || typeof obj !== "object") {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
// Check if this object is a Reference
|
|
15
|
+
if (obj.reference && typeof obj.reference === "string") {
|
|
16
|
+
references.push({
|
|
17
|
+
field: path || "reference",
|
|
18
|
+
reference: obj.reference,
|
|
19
|
+
});
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// Recursively search object properties
|
|
23
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
24
|
+
const currentPath = path ? `${path}.${key}` : key;
|
|
25
|
+
if (Array.isArray(value)) {
|
|
26
|
+
value.forEach((item, index) => {
|
|
27
|
+
searchObject(item, `${currentPath}[${index}]`);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
else if (value && typeof value === "object") {
|
|
31
|
+
searchObject(value, currentPath);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
searchObject(resource);
|
|
36
|
+
return references;
|
|
37
|
+
}
|
|
38
|
+
exports.findAllReferences = findAllReferences;
|
|
39
|
+
/**
|
|
40
|
+
* Extract target resource ID from a reference string
|
|
41
|
+
*/
|
|
42
|
+
function extractTargetIdFromReference(reference) {
|
|
43
|
+
if (!reference) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
// Handle "ResourceType/id" pattern
|
|
47
|
+
if (reference.includes("/")) {
|
|
48
|
+
const parts = reference.split("/");
|
|
49
|
+
return parts[parts.length - 1];
|
|
50
|
+
}
|
|
51
|
+
// Handle other reference patterns (e.g., "urn:uuid:xxx")
|
|
52
|
+
return reference;
|
|
53
|
+
}
|
|
54
|
+
exports.extractTargetIdFromReference = extractTargetIdFromReference;
|
|
55
|
+
/**
|
|
56
|
+
* Normalize reference field path to base field name
|
|
57
|
+
* e.g., "participant.individual" -> "participant"
|
|
58
|
+
* "performer[0]" -> "performer"
|
|
59
|
+
*/
|
|
60
|
+
function normalizeReferenceField(field) {
|
|
61
|
+
// Remove array indices
|
|
62
|
+
const withoutIndices = field.replace(/\[\d+\]/g, "");
|
|
63
|
+
// Take first part of dotted path
|
|
64
|
+
const parts = withoutIndices.split(".");
|
|
65
|
+
return parts[0] ?? field;
|
|
66
|
+
}
|
|
67
|
+
exports.normalizeReferenceField = normalizeReferenceField;
|
|
68
|
+
/**
|
|
69
|
+
* Check if a reference can be resolved within the bundle
|
|
70
|
+
* @param reference - The reference string to check
|
|
71
|
+
* @param resourcesById - Map of resources indexed by resource.id
|
|
72
|
+
* @param resourcesByFullUrl - Map of resources indexed by entry.fullUrl
|
|
73
|
+
*/
|
|
74
|
+
function canResolveReference(reference, resourcesById, resourcesByFullUrl) {
|
|
75
|
+
// Try to resolve by resource ID (e.g., "Patient/123")
|
|
76
|
+
if (reference.includes("/")) {
|
|
77
|
+
const [, resourceId] = reference.split("/");
|
|
78
|
+
if (resourceId && resourcesById.has(resourceId)) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Try to resolve by fullUrl (e.g., "urn:uuid:123")
|
|
83
|
+
if (resourcesByFullUrl.has(reference)) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
exports.canResolveReference = canResolveReference;
|
|
89
|
+
//# sourceMappingURL=reference-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reference-utils.js","sourceRoot":"","sources":["../../src/internal/reference-utils.ts"],"names":[],"mappings":";;;AAEA;;GAEG;AACH,SAAgB,iBAAiB,CAAC,QAAkB;IAClD,MAAM,UAAU,GAAgD,EAAE,CAAC;IAEnE,8DAA8D;IAC9D,SAAS,YAAY,CAAC,GAAQ,EAAE,IAAI,GAAG,EAAE;QACvC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE;YACnC,OAAO;SACR;QAED,sCAAsC;QACtC,IAAI,GAAG,CAAC,SAAS,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,EAAE;YACtD,UAAU,CAAC,IAAI,CAAC;gBACd,KAAK,EAAE,IAAI,IAAI,WAAW;gBAC1B,SAAS,EAAE,GAAG,CAAC,SAAS;aACzB,CAAC,CAAC;YACH,OAAO;SACR;QAED,uCAAuC;QACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;YAElD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;gBACxB,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;oBAC5B,YAAY,CAAC,IAAI,EAAE,GAAG,WAAW,IAAI,KAAK,GAAG,CAAC,CAAC;gBACjD,CAAC,CAAC,CAAC;aACJ;iBAAM,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;gBAC7C,YAAY,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;aAClC;SACF;IACH,CAAC;IAED,YAAY,CAAC,QAAQ,CAAC,CAAC;IACvB,OAAO,UAAU,CAAC;AACpB,CAAC;AAlCD,8CAkCC;AAED;;GAEG;AACH,SAAgB,4BAA4B,CAAC,SAAiB;IAC5D,IAAI,CAAC,SAAS,EAAE;QACd,OAAO,SAAS,CAAC;KAClB;IAED,mCAAmC;IACnC,IAAI,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;QAC3B,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnC,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;KAChC;IAED,yDAAyD;IACzD,OAAO,SAAS,CAAC;AACnB,CAAC;AAbD,oEAaC;AAED;;;;GAIG;AACH,SAAgB,uBAAuB,CAAC,KAAa;IACnD,uBAAuB;IACvB,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACrD,iCAAiC;IACjC,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxC,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;AAC3B,CAAC;AAND,0DAMC;AAED;;;;;GAKG;AACH,SAAgB,mBAAmB,CACjC,SAAiB,EACjB,aAAoC,EACpC,kBAAyC;IAEzC,sDAAsD;IACtD,IAAI,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;QAC3B,MAAM,CAAC,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5C,IAAI,UAAU,IAAI,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE;YAC/C,OAAO,IAAI,CAAC;SACb;KACF;IAED,mDAAmD;IACnD,IAAI,kBAAkB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;QACrC,OAAO,IAAI,CAAC;KACb;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAnBD,kDAmBC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Bundle, Resource } from "@medplum/fhirtypes";
|
|
2
|
+
import { ValidationResult } from "../types/sdk-types";
|
|
3
|
+
/**
|
|
4
|
+
* Validate all references in the bundle
|
|
5
|
+
* FR-2.1: Validate all references in the bundle
|
|
6
|
+
* FR-2.2: Identifies references by Resource/id pattern and fullUrl references
|
|
7
|
+
* FR-2.3: Handles both relative and absolute references
|
|
8
|
+
* FR-2.4: Returns validation result with broken reference details
|
|
9
|
+
*
|
|
10
|
+
* @param bundle - The FHIR bundle to validate
|
|
11
|
+
* @param resourcesById - Map of resources indexed by resource.id
|
|
12
|
+
* @param resourcesByFullUrl - Map of resources indexed by entry.fullUrl
|
|
13
|
+
* @returns ValidationResult with broken reference details
|
|
14
|
+
*/
|
|
15
|
+
export declare function lookForBrokenReferences(bundle: Bundle, resourcesById: Map<string, Resource>, resourcesByFullUrl: Map<string, Resource>): ValidationResult;
|
|
16
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/internal/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAmB,MAAM,oBAAoB,CAAC;AAGvE;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EACpC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,GACxC,gBAAgB,CA+BlB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.lookForBrokenReferences = void 0;
|
|
4
|
+
const reference_utils_1 = require("./reference-utils");
|
|
5
|
+
/**
|
|
6
|
+
* Validate all references in the bundle
|
|
7
|
+
* FR-2.1: Validate all references in the bundle
|
|
8
|
+
* FR-2.2: Identifies references by Resource/id pattern and fullUrl references
|
|
9
|
+
* FR-2.3: Handles both relative and absolute references
|
|
10
|
+
* FR-2.4: Returns validation result with broken reference details
|
|
11
|
+
*
|
|
12
|
+
* @param bundle - The FHIR bundle to validate
|
|
13
|
+
* @param resourcesById - Map of resources indexed by resource.id
|
|
14
|
+
* @param resourcesByFullUrl - Map of resources indexed by entry.fullUrl
|
|
15
|
+
* @returns ValidationResult with broken reference details
|
|
16
|
+
*/
|
|
17
|
+
function lookForBrokenReferences(bundle, resourcesById, resourcesByFullUrl) {
|
|
18
|
+
const brokenReferences = [];
|
|
19
|
+
if (!bundle.entry) {
|
|
20
|
+
return { hasBrokenReferences: false, brokenReferences: [] };
|
|
21
|
+
}
|
|
22
|
+
for (const entry of bundle.entry) {
|
|
23
|
+
if (!entry.resource) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const resource = entry.resource;
|
|
27
|
+
const resourceReferences = (0, reference_utils_1.findAllReferences)(resource);
|
|
28
|
+
for (const { field, reference } of resourceReferences) {
|
|
29
|
+
if (!(0, reference_utils_1.canResolveReference)(reference, resourcesById, resourcesByFullUrl)) {
|
|
30
|
+
brokenReferences.push({
|
|
31
|
+
sourceResourceId: resource.id || entry.fullUrl || "unknown",
|
|
32
|
+
sourceResourceType: resource.resourceType,
|
|
33
|
+
referenceField: field,
|
|
34
|
+
reference: reference,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
hasBrokenReferences: brokenReferences.length > 0,
|
|
41
|
+
brokenReferences: brokenReferences,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
exports.lookForBrokenReferences = lookForBrokenReferences;
|
|
45
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/internal/validation.ts"],"names":[],"mappings":";;;AAEA,uDAA2E;AAE3E;;;;;;;;;;;GAWG;AACH,SAAgB,uBAAuB,CACrC,MAAc,EACd,aAAoC,EACpC,kBAAyC;IAEzC,MAAM,gBAAgB,GAAsB,EAAE,CAAC;IAE/C,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE;QACjB,OAAO,EAAE,mBAAmB,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE,EAAE,CAAC;KAC7D;IAED,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,KAAK,EAAE;QAChC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE;YACnB,SAAS;SACV;QAED,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAChC,MAAM,kBAAkB,GAAG,IAAA,mCAAiB,EAAC,QAAQ,CAAC,CAAC;QAEvD,KAAK,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,kBAAkB,EAAE;YACrD,IAAI,CAAC,IAAA,qCAAmB,EAAC,SAAS,EAAE,aAAa,EAAE,kBAAkB,CAAC,EAAE;gBACtE,gBAAgB,CAAC,IAAI,CAAC;oBACpB,gBAAgB,EAAE,QAAQ,CAAC,EAAE,IAAI,KAAK,CAAC,OAAO,IAAI,SAAS;oBAC3D,kBAAkB,EAAE,QAAQ,CAAC,YAAY;oBACzC,cAAc,EAAE,KAAK;oBACrB,SAAS,EAAE,SAAS;iBACrB,CAAC,CAAC;aACJ;SACF;KACF;IAED,OAAO;QACL,mBAAmB,EAAE,gBAAgB,CAAC,MAAM,GAAG,CAAC;QAChD,gBAAgB,EAAE,gBAAgB;KACnC,CAAC;AACJ,CAAC;AAnCD,0DAmCC"}
|