@metriport/fhir-sdk 1.2.7 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +207 -0
  2. package/dist/__tests__/clinical-dates.test.d.ts +2 -0
  3. package/dist/__tests__/clinical-dates.test.d.ts.map +1 -0
  4. package/dist/__tests__/clinical-dates.test.js +341 -0
  5. package/dist/__tests__/clinical-dates.test.js.map +1 -0
  6. package/dist/__tests__/date-range-performance.test.d.ts +2 -0
  7. package/dist/__tests__/date-range-performance.test.d.ts.map +1 -0
  8. package/dist/__tests__/date-range-performance.test.js +218 -0
  9. package/dist/__tests__/date-range-performance.test.js.map +1 -0
  10. package/dist/__tests__/date-range-search.test.d.ts +2 -0
  11. package/dist/__tests__/date-range-search.test.d.ts.map +1 -0
  12. package/dist/__tests__/date-range-search.test.js +215 -0
  13. package/dist/__tests__/date-range-search.test.js.map +1 -0
  14. package/dist/__tests__/fhir-bundle-sdk.test.js +467 -0
  15. package/dist/__tests__/fhir-bundle-sdk.test.js.map +1 -1
  16. package/dist/__tests__/reverse-references.test.d.ts +2 -0
  17. package/dist/__tests__/reverse-references.test.d.ts.map +1 -0
  18. package/dist/__tests__/reverse-references.test.js +241 -0
  19. package/dist/__tests__/reverse-references.test.js.map +1 -0
  20. package/dist/clinical-dates.d.ts +26 -0
  21. package/dist/clinical-dates.d.ts.map +1 -0
  22. package/dist/clinical-dates.js +571 -0
  23. package/dist/clinical-dates.js.map +1 -0
  24. package/dist/demo-reverse-references.d.ts +7 -0
  25. package/dist/demo-reverse-references.d.ts.map +1 -0
  26. package/dist/demo-reverse-references.js +207 -0
  27. package/dist/demo-reverse-references.js.map +1 -0
  28. package/dist/fhir-bundle-sdk.d.ts +222 -0
  29. package/dist/fhir-bundle-sdk.d.ts.map +1 -0
  30. package/dist/fhir-bundle-sdk.js +527 -0
  31. package/dist/fhir-bundle-sdk.js.map +1 -0
  32. package/dist/index.d.ts +3 -242
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +5 -678
  35. package/dist/index.js.map +1 -1
  36. package/dist/internal/bundle-operations.d.ts +14 -0
  37. package/dist/internal/bundle-operations.d.ts.map +1 -0
  38. package/dist/internal/bundle-operations.js +55 -0
  39. package/dist/internal/bundle-operations.js.map +1 -0
  40. package/dist/internal/date-extraction.d.ts +12 -0
  41. package/dist/internal/date-extraction.d.ts.map +1 -0
  42. package/dist/internal/date-extraction.js +629 -0
  43. package/dist/internal/date-extraction.js.map +1 -0
  44. package/dist/internal/graph-traversal.d.ts +13 -0
  45. package/dist/internal/graph-traversal.d.ts.map +1 -0
  46. package/dist/internal/graph-traversal.js +89 -0
  47. package/dist/internal/graph-traversal.js.map +1 -0
  48. package/dist/internal/indexing.d.ts +17 -0
  49. package/dist/internal/indexing.d.ts.map +1 -0
  50. package/dist/internal/indexing.js +118 -0
  51. package/dist/internal/indexing.js.map +1 -0
  52. package/dist/internal/llm-context.d.ts +39 -0
  53. package/dist/internal/llm-context.d.ts.map +1 -0
  54. package/dist/internal/llm-context.js +188 -0
  55. package/dist/internal/llm-context.js.map +1 -0
  56. package/dist/internal/reference-resolution.d.ts +25 -0
  57. package/dist/internal/reference-resolution.d.ts.map +1 -0
  58. package/dist/internal/reference-resolution.js +133 -0
  59. package/dist/internal/reference-resolution.js.map +1 -0
  60. package/dist/internal/reference-utils.d.ts +26 -0
  61. package/dist/internal/reference-utils.d.ts.map +1 -0
  62. package/dist/internal/reference-utils.js +89 -0
  63. package/dist/internal/reference-utils.js.map +1 -0
  64. package/dist/internal/validation.d.ts +16 -0
  65. package/dist/internal/validation.d.ts.map +1 -0
  66. package/dist/internal/validation.js +45 -0
  67. package/dist/internal/validation.js.map +1 -0
  68. package/dist/types/sdk-types.d.ts +107 -0
  69. package/dist/types/sdk-types.d.ts.map +1 -0
  70. package/dist/types/sdk-types.js +17 -0
  71. package/dist/types/sdk-types.js.map +1 -0
  72. package/dist/types/smart-resources.d.ts +97 -4
  73. package/dist/types/smart-resources.d.ts.map +1 -1
  74. package/dist/types/smart-resources.js +82 -5
  75. package/dist/types/smart-resources.js.map +1 -1
  76. package/dist/utils/interval-tree/index.d.ts +87 -0
  77. package/dist/utils/interval-tree/index.d.ts.map +1 -0
  78. package/dist/utils/interval-tree/index.js +774 -0
  79. package/dist/utils/interval-tree/index.js.map +1 -0
  80. package/dist/utils/interval-tree/shallowequal/arrays.d.ts +3 -0
  81. package/dist/utils/interval-tree/shallowequal/arrays.d.ts.map +1 -0
  82. package/dist/utils/interval-tree/shallowequal/arrays.js +25 -0
  83. package/dist/utils/interval-tree/shallowequal/arrays.js.map +1 -0
  84. package/dist/utils/interval-tree/shallowequal/index.d.ts +6 -0
  85. package/dist/utils/interval-tree/shallowequal/index.d.ts.map +1 -0
  86. package/dist/utils/interval-tree/shallowequal/index.js +28 -0
  87. package/dist/utils/interval-tree/shallowequal/index.js.map +1 -0
  88. package/dist/utils/interval-tree/shallowequal/objects.d.ts +3 -0
  89. package/dist/utils/interval-tree/shallowequal/objects.d.ts.map +1 -0
  90. package/dist/utils/interval-tree/shallowequal/objects.js +28 -0
  91. package/dist/utils/interval-tree/shallowequal/objects.js.map +1 -0
  92. package/package.json +1 -1
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.walkReferences = void 0;
4
+ const smart_resources_1 = require("../types/smart-resources");
5
+ /**
6
+ * Walk references from a starting resource using BFS traversal.
7
+ * Discovers all reachable resources up to maxDepth levels.
8
+ *
9
+ * @param startResource - The smart resource to start traversal from
10
+ * @param options - Walk options including maxDepth and includeStartResource
11
+ * @returns WalkResult containing all discovered resources organized by depth
12
+ */
13
+ function walkReferences(startResource, options) {
14
+ const maxDepth = options?.maxDepth ?? Infinity;
15
+ const includeStartResource = options?.includeStartResource ?? true;
16
+ const resourcesByDepth = new Map();
17
+ const visited = new Set();
18
+ const queue = [];
19
+ // Initialize with start resource at depth 0
20
+ queue.push({ resource: startResource, depth: 0 });
21
+ if (startResource.id) {
22
+ visited.add(startResource.id);
23
+ }
24
+ let maxDepthReached = 0;
25
+ while (queue.length > 0) {
26
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
27
+ const { resource, depth } = queue.shift();
28
+ // Track max depth reached
29
+ if (depth > maxDepthReached) {
30
+ maxDepthReached = depth;
31
+ }
32
+ // Add resource to current depth level
33
+ if (!resourcesByDepth.has(depth)) {
34
+ resourcesByDepth.set(depth, []);
35
+ }
36
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
37
+ resourcesByDepth.get(depth).push(resource);
38
+ // Stop if we've reached max depth
39
+ if (depth >= maxDepth) {
40
+ continue;
41
+ }
42
+ // Get all reference methods for this resource type
43
+ const referenceMethods = smart_resources_1.REFERENCE_METHOD_MAPPING[resource.resourceType];
44
+ if (!referenceMethods) {
45
+ continue;
46
+ }
47
+ // Iterate through all reference methods
48
+ for (const methodName of Object.keys(referenceMethods)) {
49
+ // Call the reference method
50
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
+ const referencedResources = resource[methodName]();
52
+ if (!referencedResources) {
53
+ continue;
54
+ }
55
+ // Handle both single and array references
56
+ const resourceArray = Array.isArray(referencedResources)
57
+ ? referencedResources
58
+ : [referencedResources];
59
+ for (const referencedResource of resourceArray) {
60
+ if (!referencedResource?.id) {
61
+ continue;
62
+ }
63
+ // Skip if already visited
64
+ if (visited.has(referencedResource.id)) {
65
+ continue;
66
+ }
67
+ visited.add(referencedResource.id);
68
+ queue.push({ resource: referencedResource, depth: depth + 1 });
69
+ }
70
+ }
71
+ }
72
+ // Build flat array of all resources
73
+ const allResources = [];
74
+ for (const [depth, resources] of resourcesByDepth.entries()) {
75
+ // Skip depth 0 (start resource) if includeStartResource is false
76
+ if (depth === 0 && !includeStartResource) {
77
+ continue;
78
+ }
79
+ allResources.push(...resources);
80
+ }
81
+ return {
82
+ resourcesByDepth,
83
+ allResources,
84
+ startResource,
85
+ depthReached: maxDepthReached,
86
+ };
87
+ }
88
+ exports.walkReferences = walkReferences;
89
+ //# sourceMappingURL=graph-traversal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-traversal.js","sourceRoot":"","sources":["../../src/internal/graph-traversal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { Bundle, Resource } from "@medplum/fhirtypes";
2
+ import { ReverseReference } from "../types/sdk-types";
3
+ import { IntervalTree } from "../utils/interval-tree";
4
+ import { DateIndexRecord } from "../types/sdk-types";
5
+ /**
6
+ * Build O(1) indexes for resource lookup
7
+ */
8
+ export declare function buildResourceIndexes(bundle: Bundle, resourcesById: Map<string, Resource>, resourcesByFullUrl: Map<string, Resource>, resourcesByType: Map<string, Resource[]>, reverseReferencesById: Map<string, ReverseReference[]>, dateRangeIndex: IntervalTree<DateIndexRecord, number>, resolutionStack: Set<string>): void;
9
+ /**
10
+ * Build reverse reference index for O(1) reverse lookup
11
+ */
12
+ export declare function buildReverseReferenceIndex(bundle: Bundle, reverseReferencesById: Map<string, ReverseReference[]>, resourcesByFullUrl: Map<string, Resource>): void;
13
+ /**
14
+ * Build date range index for O(log n + k) date range searches
15
+ */
16
+ export declare function buildDateRangeIndex(bundle: Bundle, dateRangeIndex: IntervalTree<DateIndexRecord, number>): void;
17
+ //# sourceMappingURL=indexing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexing.d.ts","sourceRoot":"","sources":["../../src/internal/indexing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAQrD;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EACpC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EACzC,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,EACxC,qBAAqB,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EACtD,cAAc,EAAE,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,EACrD,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,GAC3B,IAAI,CAuCN;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,EACd,qBAAqB,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EACtD,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,GACxC,IAAI,CA6DN;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,GACpD,IAAI,CAiBN"}
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildDateRangeIndex = exports.buildReverseReferenceIndex = exports.buildResourceIndexes = void 0;
4
+ const reference_utils_1 = require("./reference-utils");
5
+ const date_extraction_1 = require("./date-extraction");
6
+ /**
7
+ * Build O(1) indexes for resource lookup
8
+ */
9
+ function buildResourceIndexes(bundle, resourcesById, resourcesByFullUrl, resourcesByType, reverseReferencesById, dateRangeIndex, resolutionStack) {
10
+ if (!bundle.entry) {
11
+ return;
12
+ }
13
+ for (const entry of bundle.entry) {
14
+ if (!entry.resource) {
15
+ continue;
16
+ }
17
+ const resource = entry.resource;
18
+ // Index by resource.id if it exists
19
+ if (resource.id) {
20
+ resourcesById.set(resource.id, resource);
21
+ }
22
+ // Index by fullUrl if it exists
23
+ if (entry.fullUrl) {
24
+ resourcesByFullUrl.set(entry.fullUrl, resource);
25
+ }
26
+ // Index by resource type for type-specific getters
27
+ const resourceType = resource.resourceType;
28
+ if (!resourcesByType.has(resourceType)) {
29
+ resourcesByType.set(resourceType, []);
30
+ }
31
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
32
+ resourcesByType.get(resourceType).push(resource);
33
+ }
34
+ // Build reverse reference index
35
+ buildReverseReferenceIndex(bundle, reverseReferencesById, resourcesByFullUrl);
36
+ // Build date range index
37
+ buildDateRangeIndex(bundle, dateRangeIndex);
38
+ // Clear resolution stack after index building
39
+ resolutionStack.clear();
40
+ }
41
+ exports.buildResourceIndexes = buildResourceIndexes;
42
+ /**
43
+ * Build reverse reference index for O(1) reverse lookup
44
+ */
45
+ function buildReverseReferenceIndex(bundle, reverseReferencesById, resourcesByFullUrl) {
46
+ if (!bundle.entry) {
47
+ return;
48
+ }
49
+ for (const entry of bundle.entry) {
50
+ if (!entry.resource) {
51
+ continue;
52
+ }
53
+ const resource = entry.resource;
54
+ const sourceResourceId = resource.id ?? entry.fullUrl ?? "unknown";
55
+ const references = (0, reference_utils_1.findAllReferences)(resource);
56
+ for (const { field, reference } of references) {
57
+ // Extract target ID from reference
58
+ const targetId = (0, reference_utils_1.extractTargetIdFromReference)(reference);
59
+ if (!targetId) {
60
+ continue;
61
+ }
62
+ // Add to reverse index for both the extracted ID and the full reference
63
+ const reverseRef = {
64
+ sourceResourceId,
65
+ sourceResourceType: resource.resourceType,
66
+ referenceField: (0, reference_utils_1.normalizeReferenceField)(field),
67
+ };
68
+ // Index by extracted resource ID (e.g., "patient-123" from "Patient/patient-123")
69
+ if (!reverseReferencesById.has(targetId)) {
70
+ reverseReferencesById.set(targetId, []);
71
+ }
72
+ reverseReferencesById.get(targetId)?.push(reverseRef);
73
+ // Also index by full reference if it's different (e.g., "urn:uuid:patient-123")
74
+ if (targetId !== reference) {
75
+ if (!reverseReferencesById.has(reference)) {
76
+ reverseReferencesById.set(reference, []);
77
+ }
78
+ reverseReferencesById.get(reference)?.push(reverseRef);
79
+ }
80
+ // Also try to index by the actual resource ID if we can find it efficiently
81
+ // Check if this reference can be resolved to a resource with a different ID
82
+ if (!reference.includes("/")) {
83
+ // This is likely a fullUrl reference (e.g., "urn:uuid:xxx")
84
+ // Check if we have a resource with this fullUrl
85
+ const targetResource = resourcesByFullUrl.get(reference);
86
+ if (targetResource?.id &&
87
+ targetResource.id !== targetId &&
88
+ targetResource.id !== reference) {
89
+ if (!reverseReferencesById.has(targetResource.id)) {
90
+ reverseReferencesById.set(targetResource.id, []);
91
+ }
92
+ reverseReferencesById.get(targetResource.id)?.push(reverseRef);
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }
98
+ exports.buildReverseReferenceIndex = buildReverseReferenceIndex;
99
+ /**
100
+ * Build date range index for O(log n + k) date range searches
101
+ */
102
+ function buildDateRangeIndex(bundle, dateRangeIndex) {
103
+ if (!bundle.entry) {
104
+ return;
105
+ }
106
+ for (const entry of bundle.entry) {
107
+ if (!entry.resource || !entry.resource.id) {
108
+ continue;
109
+ }
110
+ const resource = entry.resource;
111
+ const dateIntervals = (0, date_extraction_1.extractDateIntervalsFromResource)(resource);
112
+ for (const interval of dateIntervals) {
113
+ dateRangeIndex.insert(interval);
114
+ }
115
+ }
116
+ }
117
+ exports.buildDateRangeIndex = buildDateRangeIndex;
118
+ //# sourceMappingURL=indexing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexing.js","sourceRoot":"","sources":["../../src/internal/indexing.ts"],"names":[],"mappings":"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"}
@@ -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