vintasend-medplum 0.4.12 → 0.4.13

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.
@@ -40,7 +40,7 @@ export declare class MedplumAttachmentManager extends BaseAttachmentManager {
40
40
  /**
41
41
  * Reconstruct an AttachmentFile from storage metadata.
42
42
  *
43
- * @param storageMetadata - Metadata containing either 'url' (Binary URL) or 'binaryId'
43
+ * @param storageMetadata - Metadata containing 'binaryId'
44
44
  * @returns AttachmentFile instance for accessing the file
45
45
  */
46
46
  reconstructAttachmentFile(storageMetadata: Record<string, unknown>): AttachmentFile;
@@ -53,9 +53,13 @@ export declare class MedplumAttachmentManager extends BaseAttachmentManager {
53
53
  export declare class MedplumAttachmentFile implements AttachmentFile {
54
54
  private medplum;
55
55
  private binaryId;
56
- constructor(medplum: MedplumClient, binaryUrl: string);
56
+ constructor(medplum: MedplumClient, binaryId: string);
57
57
  /**
58
58
  * Read the entire file into memory as a Buffer.
59
+ *
60
+ * Works with Binary resources that have either:
61
+ * - Inline data (base64 encoded)
62
+ * - A presigned URL to download from
59
63
  */
60
64
  read(): Promise<Buffer>;
61
65
  /**
@@ -68,16 +72,12 @@ export declare class MedplumAttachmentFile implements AttachmentFile {
68
72
  /**
69
73
  * Generate a URL for accessing the file.
70
74
  *
71
- * For Medplum, this returns the Binary resource URL.
72
- * In production, you might want to generate a presigned URL.
75
+ * For Medplum, this returns a presigned URL from the Binary resource.
73
76
  *
74
77
  * @param expiresIn - Seconds until the URL expires (not used for Medplum)
75
- * @returns URL for file access
78
+ * @returns Presigned URL for file access
76
79
  */
77
80
  url(expiresIn?: number): Promise<string>;
78
- /**
79
- * Delete this file from storage.
80
- */
81
81
  delete(): Promise<void>;
82
82
  }
83
83
  //# sourceMappingURL=medplum-attachment-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"medplum-attachment-manager.d.ts","sourceRoot":"","sources":["../src/medplum-attachment-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,oEAAoE,CAAC;AAC3G,OAAO,KAAK,EACV,oBAAoB,EACpB,cAAc,EACd,cAAc,EACf,MAAM,iCAAiC,CAAC;AAEzC;;;;;;;;GAQG;AACH,qBAAa,wBAAyB,SAAQ,qBAAqB;IACrD,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,aAAa;IAI1C;;;;;;;OAOG;IACG,UAAU,CACd,IAAI,EAAE,cAAc,EACpB,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,oBAAoB,CAAC;IAkEhC;;;;;OAKG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IA0BnE;;;;;;OAMG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB/C;;;;;OAKG;IACH,yBAAyB,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc;CAgBpF;AAED;;;;GAIG;AACH,qBAAa,qBAAsB,YAAW,cAAc;IAIxD,OAAO,CAAC,OAAO;IAHjB,OAAO,CAAC,QAAQ,CAAS;gBAGf,OAAO,EAAE,aAAa,EAC9B,SAAS,EAAE,MAAM;IA2BnB;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAkB7B;;;;;OAKG;IACG,MAAM,IAAI,OAAO,CAAC,cAAc,CAAC;IAUvC;;;;;;;;OAQG;IACG,GAAG,CAAC,SAAS,SAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAO5C;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAG9B"}
1
+ {"version":3,"file":"medplum-attachment-manager.d.ts","sourceRoot":"","sources":["../src/medplum-attachment-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,oEAAoE,CAAC;AAC3G,OAAO,KAAK,EACV,oBAAoB,EACpB,cAAc,EACd,cAAc,EACf,MAAM,iCAAiC,CAAC;AAEzC;;;;;;;;GAQG;AACH,qBAAa,wBAAyB,SAAQ,qBAAqB;IACrD,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,aAAa;IAI1C;;;;;;;OAOG;IACG,UAAU,CACd,IAAI,EAAE,cAAc,EACpB,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,oBAAoB,CAAC;IAkEhC;;;;;OAKG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAuCnE;;;;;;OAMG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB/C;;;;;OAKG;IACH,yBAAyB,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc;CAOpF;AAED;;;;GAIG;AACH,qBAAa,qBAAsB,YAAW,cAAc;IAExD,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,QAAQ;gBADR,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,MAAM;IAG1B;;;;;;OAMG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAsB7B;;;;;OAKG;IACG,MAAM,IAAI,OAAO,CAAC,cAAc,CAAC;IAUvC;;;;;;;OAOG;IACG,GAAG,CAAC,SAAS,SAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAWtC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAG9B"}
@@ -91,14 +91,21 @@ export class MedplumAttachmentManager extends BaseAttachmentManager {
91
91
  if (!media.content || !media.id) {
92
92
  return null;
93
93
  }
94
+ // Extract Binary ID from identifier
95
+ const binaryIdIdentifier = media.identifier?.find((id) => id.system === 'http://vintasend.com/fhir/binary-id');
96
+ const binaryId = binaryIdIdentifier?.value;
97
+ // Extract checksum from identifier
98
+ const checksumIdentifier = media.identifier?.find((id) => id.system === 'http://vintasend.com/fhir/attachment-checksum');
99
+ const checksum = checksumIdentifier?.value || '';
94
100
  return {
95
101
  id: media.id,
96
102
  filename: media.content.title || 'untitled',
97
103
  contentType: media.content.contentType || 'application/octet-stream',
98
104
  size: media.content.size || 0,
99
- checksum: media.identifier?.[0]?.value || '',
105
+ checksum,
100
106
  storageMetadata: {
101
107
  url: media.content.url,
108
+ binaryId: binaryId,
102
109
  creation: media.content.creation,
103
110
  },
104
111
  createdAt: media.meta?.lastUpdated ? new Date(media.meta.lastUpdated) : new Date(),
@@ -141,24 +148,14 @@ export class MedplumAttachmentManager extends BaseAttachmentManager {
141
148
  /**
142
149
  * Reconstruct an AttachmentFile from storage metadata.
143
150
  *
144
- * @param storageMetadata - Metadata containing either 'url' (Binary URL) or 'binaryId'
151
+ * @param storageMetadata - Metadata containing 'binaryId'
145
152
  * @returns AttachmentFile instance for accessing the file
146
153
  */
147
154
  reconstructAttachmentFile(storageMetadata) {
148
- // Support both 'url' (from upload) and 'binaryId' (direct ID)
149
- let binaryUrl;
150
- if (storageMetadata.binaryId && typeof storageMetadata.binaryId === 'string') {
151
- // Direct Binary ID format
152
- binaryUrl = `Binary/${storageMetadata.binaryId}`;
155
+ if (!storageMetadata.binaryId || typeof storageMetadata.binaryId !== 'string') {
156
+ throw new Error('Storage metadata must contain binaryId');
153
157
  }
154
- else if (storageMetadata.url && typeof storageMetadata.url === 'string') {
155
- // Binary URL format (from uploadFile)
156
- binaryUrl = storageMetadata.url;
157
- }
158
- else {
159
- throw new Error('Storage metadata must contain binaryId for Medplum files');
160
- }
161
- return new MedplumAttachmentFile(this.medplum, binaryUrl);
158
+ return new MedplumAttachmentFile(this.medplum, storageMetadata.binaryId);
162
159
  }
163
160
  }
164
161
  /**
@@ -167,49 +164,34 @@ export class MedplumAttachmentManager extends BaseAttachmentManager {
167
164
  * Provides access to files stored in Medplum Binary resources.
168
165
  */
169
166
  export class MedplumAttachmentFile {
170
- constructor(medplum, binaryUrl) {
167
+ constructor(medplum, binaryId) {
171
168
  this.medplum = medplum;
172
- // Extract Binary ID from URL
173
- // Supports two formats:
174
- // 1. Simple reference: "Binary/{id}"
175
- // 2. Full Medplum storage URL: "https://storage.medplum.com/binary/{projectId}/{binaryId}?..."
176
- console.log(`[MedplumAttachmentFile] Constructing with binaryUrl: ${binaryUrl}`);
177
- // Try to extract from full URL first
178
- const fullUrlMatch = binaryUrl.match(/\/binary\/[^/]+\/([^/?]+)/);
179
- if (fullUrlMatch) {
180
- this.binaryId = fullUrlMatch[1];
181
- console.log(`[MedplumAttachmentFile] Extracted binaryId from full URL: ${this.binaryId}`);
182
- return;
183
- }
184
- // Fall back to simple Binary reference
185
- const simpleMatch = binaryUrl.match(/Binary\/([^/]+)/);
186
- if (simpleMatch) {
187
- this.binaryId = simpleMatch[1];
188
- console.log(`[MedplumAttachmentFile] Extracted binaryId from simple reference: ${this.binaryId}`);
189
- return;
190
- }
191
- throw new Error(`Invalid Binary URL format: ${binaryUrl}`);
169
+ this.binaryId = binaryId;
192
170
  }
193
171
  /**
194
172
  * Read the entire file into memory as a Buffer.
173
+ *
174
+ * Works with Binary resources that have either:
175
+ * - Inline data (base64 encoded)
176
+ * - A presigned URL to download from
195
177
  */
196
178
  async read() {
197
- try {
198
- console.log(`[MedplumAttachmentFile.read] Reading Binary resource with ID: ${this.binaryId}`);
199
- const binary = await this.medplum.readResource('Binary', this.binaryId);
200
- console.log(`[MedplumAttachmentFile.read] Successfully fetched Binary resource: ${this.binaryId}`);
201
- if (!binary.data) {
202
- throw new Error('Binary resource has no data');
203
- }
204
- const buffer = Buffer.from(binary.data, 'base64');
205
- console.log(`[MedplumAttachmentFile.read] Successfully converted to buffer, size: ${buffer.length} bytes`);
206
- return buffer;
179
+ const binary = await this.medplum.readResource('Binary', this.binaryId);
180
+ // If data is embedded in the Binary resource, use it directly
181
+ if (binary.data) {
182
+ return Buffer.from(binary.data, 'base64');
207
183
  }
208
- catch (error) {
209
- console.error(`[MedplumAttachmentFile.read] Error reading Binary ${this.binaryId}:`, error);
210
- console.error(`[MedplumAttachmentFile.read] Error details:`, JSON.stringify(error, null, 2));
211
- throw error;
184
+ // If data is not embedded but a URL is available, download it
185
+ if (binary.url) {
186
+ const response = await fetch(binary.url);
187
+ if (!response.ok) {
188
+ throw new Error(`Failed to download binary from URL: ${response.statusText}`);
189
+ }
190
+ const arrayBuffer = await response.arrayBuffer();
191
+ return Buffer.from(arrayBuffer);
212
192
  }
193
+ // Neither data nor URL available
194
+ throw new Error('Binary resource has neither data nor url');
213
195
  }
214
196
  /**
215
197
  * Get a readable stream for the file.
@@ -229,21 +211,19 @@ export class MedplumAttachmentFile {
229
211
  /**
230
212
  * Generate a URL for accessing the file.
231
213
  *
232
- * For Medplum, this returns the Binary resource URL.
233
- * In production, you might want to generate a presigned URL.
214
+ * For Medplum, this returns a presigned URL from the Binary resource.
234
215
  *
235
216
  * @param expiresIn - Seconds until the URL expires (not used for Medplum)
236
- * @returns URL for file access
217
+ * @returns Presigned URL for file access
237
218
  */
238
219
  async url(expiresIn = 3600) {
239
- // For Medplum, we can use the Binary resource URL
240
- // In a production setup, you might want to use Medplum's authentication
241
- // or generate a time-limited access token
242
- return `Binary/${this.binaryId}`;
220
+ // Fetch the Binary resource to get the presigned URL
221
+ const binary = await this.medplum.readResource('Binary', this.binaryId);
222
+ if (!binary.url) {
223
+ throw new Error(`Binary resource ${this.binaryId} does not have a presigned URL`);
224
+ }
225
+ return binary.url;
243
226
  }
244
- /**
245
- * Delete this file from storage.
246
- */
247
227
  async delete() {
248
228
  await this.medplum.deleteResource('Binary', this.binaryId);
249
229
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vintasend-medplum",
3
- "version": "0.4.12",
3
+ "version": "0.4.13",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {