@meistrari/vault-sdk 1.0.1 → 1.2.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/dist/index.mjs CHANGED
@@ -1,4 +1,35 @@
1
1
  import { GetUploadUrlResponseV2, GetDownloadUrlResponse } from '@meistrari/vault-shared/schemas';
2
+ import { fileTypeFromBlob } from 'file-type';
3
+ import { lookup } from 'mime-types';
4
+
5
+ var __defProp$1 = Object.defineProperty;
6
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
+ var __publicField$1 = (obj, key, value) => {
8
+ __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
9
+ return value;
10
+ };
11
+ class DataTokenAuthStrategy {
12
+ constructor(dataToken) {
13
+ __publicField$1(this, "dataToken");
14
+ this.dataToken = dataToken;
15
+ }
16
+ getHeaders() {
17
+ return new Headers({
18
+ "x-data-token": this.dataToken
19
+ });
20
+ }
21
+ }
22
+ class APIKeyAuthStrategy {
23
+ constructor(apiKey) {
24
+ __publicField$1(this, "apiKey");
25
+ this.apiKey = apiKey;
26
+ }
27
+ getHeaders() {
28
+ return new Headers({
29
+ Authorization: this.apiKey
30
+ });
31
+ }
32
+ }
2
33
 
3
34
  class FetchError extends Error {
4
35
  constructor(message, url, method, response) {
@@ -32,11 +63,30 @@ async function getFileHash(blob) {
32
63
  const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
33
64
  return hashHex;
34
65
  }
66
+ async function detectFileMimeType(blob) {
67
+ const result = await fileTypeFromBlob(blob).catch(() => void 0);
68
+ if (result?.mime) {
69
+ return result.mime;
70
+ }
71
+ if ("name" in blob && typeof blob.name === "string") {
72
+ const extension = blob.name.split(".").pop()?.toLowerCase();
73
+ if (extension) {
74
+ const mimeType = lookup(`.${extension}`);
75
+ if (mimeType) {
76
+ return mimeType;
77
+ }
78
+ }
79
+ }
80
+ if (blob instanceof Blob && blob.type) {
81
+ return blob.type;
82
+ }
83
+ return void 0;
84
+ }
35
85
 
36
- var __defProp$1 = Object.defineProperty;
37
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
38
- var __publicField$1 = (obj, key, value) => {
39
- __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
86
+ var __defProp = Object.defineProperty;
87
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
88
+ var __publicField = (obj, key, value) => {
89
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
40
90
  return value;
41
91
  };
42
92
  const compatibilityDate = "2025-05-19";
@@ -65,13 +115,13 @@ class VaultFile {
65
115
  * @param params.metadata - The metadata of the file
66
116
  */
67
117
  constructor({ config, content, id, name, metadata }) {
68
- __publicField$1(this, "id");
69
- __publicField$1(this, "name");
70
- __publicField$1(this, "metadata");
71
- __publicField$1(this, "config");
72
- __publicField$1(this, "content");
73
- __publicField$1(this, "lastDownloadUrl");
74
- __publicField$1(this, "lastUploadUrl");
118
+ __publicField(this, "id");
119
+ __publicField(this, "name");
120
+ __publicField(this, "metadata");
121
+ __publicField(this, "config");
122
+ __publicField(this, "content");
123
+ __publicField(this, "lastDownloadUrl");
124
+ __publicField(this, "lastUploadUrl");
75
125
  this.config = config;
76
126
  this.content = content;
77
127
  this.id = id;
@@ -93,37 +143,46 @@ class VaultFile {
93
143
  * @param params.method - The method to use for the fetch
94
144
  * @param params.path - The path to fetch
95
145
  * @param params.body - The body of the request
146
+ * @param params.signal - The signal to abort the request
96
147
  * @returns The response from the vault
97
148
  * @throws {FetchError} If the fetch fails
98
149
  */
99
150
  async _fetch(params) {
100
- const { method, path, body } = params;
151
+ const { method, path, body, signal } = params;
101
152
  const url = new URL(this.config.vaultUrl + path).toString();
102
153
  const headers = new Headers(this.headers);
103
154
  headers.set("x-compatibility-date", compatibilityDate);
104
155
  const response = await wrappedFetch(url, {
105
156
  method,
106
157
  body,
107
- headers
158
+ headers,
159
+ signal
108
160
  });
109
161
  const content = await response.json();
110
162
  return content;
111
163
  }
112
164
  /**
113
165
  * Creates a new file in the vault.
166
+ * @param metadata - The metadata for creating a file
167
+ * @param metadata.size - The size of the file
168
+ * @param metadata.mimeType - The mime type of the file
169
+ * @param options - The options for the request
170
+ * @param options.signal - The signal to abort the request
114
171
  *
115
172
  * @returns The metadata of the file
116
173
  * @throws {Error} If the file ID is not set
117
174
  * @throws {FetchError} If the metadata fetch fails
118
175
  */
119
- async _createFile() {
176
+ async _createFile(metadata = {}, options) {
120
177
  const response = await this._fetch({
121
178
  method: "POST",
122
179
  path: `/v2/files`,
123
180
  body: JSON.stringify({
181
+ ...metadata,
124
182
  fileName: this.name,
125
183
  sha256sum: this.id ?? this.metadata?.id ?? (this.content ? await getFileHash(this.content) : void 0)
126
- })
184
+ }),
185
+ signal: options?.signal
127
186
  }).then((data) => GetUploadUrlResponseV2.safeParse(data));
128
187
  if (!response.success) {
129
188
  throw new Error(`Invalid response from vault service. ${JSON.stringify(response.error)}`);
@@ -140,6 +199,9 @@ class VaultFile {
140
199
  * @param params.reference - The reference to the file in the vault
141
200
  * @param params.config - The configuration for the VaultFile
142
201
  * @param params.download - Whether to download the file content (default: false)
202
+ * @param options - The options for the request
203
+ * @param options.signal - The signal to abort the request
204
+ *
143
205
  * @returns A new VaultFile instance
144
206
  *
145
207
  * @example
@@ -169,13 +231,14 @@ class VaultFile {
169
231
  * const content = vaultFile.content
170
232
  * ```
171
233
  */
172
- static async fromVaultReference(params) {
234
+ static async fromVaultReference(params, options) {
173
235
  const { reference, config, download = false } = params;
174
236
  const { vaultUrl, authStrategy } = config;
175
237
  const id = removeVaultPrefix(reference);
176
238
  const response = await wrappedFetch(`${vaultUrl}/v2/files/${id}`, {
177
239
  method: "GET",
178
- headers: authStrategy.getHeaders()
240
+ headers: authStrategy.getHeaders(),
241
+ signal: options?.signal
179
242
  }).then((response2) => response2.json()).then((data) => GetDownloadUrlResponse.safeParse(data));
180
243
  if (!response.success) {
181
244
  throw new Error("Invalid response from vault service");
@@ -190,7 +253,7 @@ class VaultFile {
190
253
  name: response.data.metadata?.originalFileName
191
254
  };
192
255
  if (download) {
193
- await wrappedFetch(response.data.url, { method: "GET" }).then((response2) => response2.blob()).then((blob) => fileParams.content = blob);
256
+ await wrappedFetch(response.data.url, { method: "GET", signal: options?.signal }).then((response2) => response2.blob()).then((blob) => fileParams.content = blob);
194
257
  }
195
258
  return new VaultFile(fileParams);
196
259
  }
@@ -202,6 +265,9 @@ class VaultFile {
202
265
  * @param params.content - The content of the file
203
266
  * @param params.config - The configuration for the VaultFile
204
267
  * @param params.upload - Whether to upload the file (default: false)
268
+ * @param options - The options for the request
269
+ * @param options.signal - The signal to abort the request
270
+ *
205
271
  * @returns A new VaultFile instance
206
272
  *
207
273
  * @example
@@ -234,10 +300,12 @@ class VaultFile {
234
300
  * })
235
301
  * ```
236
302
  */
237
- static async fromContent(params) {
303
+ static async fromContent(params, options) {
238
304
  const { name, content, config, upload = false } = params;
239
305
  const { vaultUrl, authStrategy } = config;
240
306
  const sha256sum = await getFileHash(content);
307
+ const mimeType = await detectFileMimeType(content);
308
+ const size = content.size;
241
309
  const file = new VaultFile({
242
310
  content,
243
311
  config: {
@@ -247,22 +315,27 @@ class VaultFile {
247
315
  id: sha256sum,
248
316
  name
249
317
  });
250
- const createdFile = await file._createFile();
318
+ const createdFile = await file._createFile({
319
+ size,
320
+ mimeType
321
+ }, { signal: options?.signal });
251
322
  if (upload) {
252
- await file.upload(file.content, createdFile.uploadUrl);
323
+ await file.upload(file.content, createdFile.uploadUrl, { signal: options?.signal });
253
324
  }
254
325
  return file;
255
326
  }
256
327
  /**
257
328
  * Populates the metadata of the file instance.
329
+ * @param options - The options for the request
330
+ * @param options.signal - The signal to abort the request
258
331
  *
259
332
  * @returns The file instance
260
333
  * @throws {Error} If the file ID is not set
261
334
  * @throws {FetchError} If the metadata fetch fails
262
335
  */
263
- async populateMetadata() {
336
+ async populateMetadata(options) {
264
337
  try {
265
- this.metadata = await this.getFileMetadata();
338
+ this.metadata = await this.getFileMetadata({ signal: options?.signal });
266
339
  this.name = this.metadata.originalFileName;
267
340
  this.id = this.metadata.id;
268
341
  return this;
@@ -284,29 +357,34 @@ class VaultFile {
284
357
  }
285
358
  /**
286
359
  * Fetches the metadata of the file.
360
+ * @param options - The options for the request
361
+ * @param options.signal - The signal to abort the request
287
362
  *
288
363
  * @returns The metadata of the file
289
364
  * @throws {Error} If the file ID is not set
290
365
  * @throws {FetchError} If the metadata fetch fails
291
366
  */
292
- async getFileMetadata() {
367
+ async getFileMetadata(options) {
293
368
  if (!this.id) {
294
369
  throw new Error("File ID is not set");
295
370
  }
296
371
  const response = await this._fetch({
297
372
  method: "GET",
298
- path: `/v2/files/${this.id}/metadata`
373
+ path: `/v2/files/${this.id}/metadata`,
374
+ signal: options?.signal
299
375
  });
300
376
  return response;
301
377
  }
302
378
  /**
303
379
  * Fetches a upload URL for the file.
380
+ * @param options - The options for the request
381
+ * @param options.signal - The signal to abort the request
304
382
  *
305
383
  * @returns The upload URL for the file
306
384
  * @throws {Error} If the vault service returns an invalid response
307
385
  * @throws {FetchError} If the upload URL fetch fails
308
386
  */
309
- async getUploadUrl() {
387
+ async getUploadUrl(options) {
310
388
  if (this.lastUploadUrl && this.lastUploadUrl.expiresAt > /* @__PURE__ */ new Date()) {
311
389
  return this.lastUploadUrl.url;
312
390
  }
@@ -320,7 +398,8 @@ class VaultFile {
320
398
  }
321
399
  const response = await this._fetch({
322
400
  method: "PUT",
323
- path: `/v2/files/${this.id}`
401
+ path: `/v2/files/${this.id}`,
402
+ signal: options?.signal
324
403
  }).then(GetUploadUrlResponseV2.safeParse);
325
404
  if (!response.success) {
326
405
  throw new Error(`Invalid response from vault service. ${JSON.stringify(response.error)}`);
@@ -330,13 +409,15 @@ class VaultFile {
330
409
  }
331
410
  /**
332
411
  * Fetches a download URL for the file.
412
+ * @param options - The options for the request
413
+ * @param options.signal - The signal to abort the request
333
414
  *
334
415
  * @returns The download URL for the file
335
416
  * @throws {Error} If the vault service returns an invalid response
336
417
  * @throws {Error} If not file ID, name or content is set
337
418
  * @throws {FetchError} If the download URL fetch fails
338
419
  */
339
- async getDownloadUrl() {
420
+ async getDownloadUrl(options) {
340
421
  if (this.lastDownloadUrl && this.lastDownloadUrl.expiresAt > /* @__PURE__ */ new Date()) {
341
422
  return this.lastDownloadUrl.url;
342
423
  }
@@ -346,7 +427,8 @@ class VaultFile {
346
427
  const id = this.id ?? this.metadata?.id ?? (this.content ? await getFileHash(this.content) : this.name);
347
428
  const response = await this._fetch({
348
429
  method: "GET",
349
- path: `/v2/files/${id}`
430
+ path: `/v2/files/${id}`,
431
+ signal: options?.signal
350
432
  });
351
433
  this.lastDownloadUrl = { url: new URL(response.url), expiresAt: new Date(response.expiresAt) };
352
434
  return this.lastDownloadUrl.url;
@@ -357,29 +439,48 @@ class VaultFile {
357
439
  * @example
358
440
  * ```ts
359
441
  * const file = new File(['content'], 'document.txt')
360
- * const vaultFile = await VaultFile.fromBlob('document.txt', file, { vaultUrl, authStrategy })
442
+ * const vaultFile = await VaultFile.fromContent({
443
+ * name: 'document.txt',
444
+ * content: file,
445
+ * config: {
446
+ * vaultUrl,
447
+ * authStrategy,
448
+ * },
449
+ * })
361
450
  * await vaultFile.upload(file)
362
451
  * ```
363
452
  *
364
453
  * @param file - The file to upload to the vault. If not provided, the file content will be taken from the `content` property.
454
+ * @param url - The URL to upload the file to. If not provided, the upload URL will be fetched from the vault.
455
+ * @param options - The options for the request
456
+ * @param options.signal - The signal to abort the request
457
+ *
365
458
  * @throws {FetchError} If the upload fails
366
459
  * @throws {Error} If the file content is not set and no file is provided
367
460
  * @returns Promise that resolves when upload is complete
368
461
  */
369
- async upload(file, url) {
370
- if (!file && !this.content) {
371
- throw new Error("Missing file content. Use fromBlob() to create a file with content, or provide a file to upload.");
462
+ async upload(file, url, options) {
463
+ const content = file ?? this.content;
464
+ if (!content) {
465
+ throw new Error("Missing file content. Use fromContent() to create a file with content, or provide a file to upload.");
372
466
  }
373
467
  const uploadUrl = url ?? await this.getUploadUrl();
468
+ const mimeType = this.metadata?.mimeType ?? await detectFileMimeType(content);
469
+ const headers = new Headers();
470
+ if (mimeType)
471
+ headers.set("Content-Type", mimeType);
374
472
  await wrappedFetch(uploadUrl, {
375
473
  method: "PUT",
376
- body: file ?? this.content
474
+ body: content,
475
+ headers,
476
+ signal: options?.signal
377
477
  });
378
478
  }
379
- async download(responseType = "blob") {
380
- const downloadUrl = await this.getDownloadUrl();
479
+ async download(responseType = "blob", options) {
480
+ const downloadUrl = await this.getDownloadUrl({ signal: options?.signal });
381
481
  const response = await wrappedFetch(downloadUrl, {
382
- method: "GET"
482
+ method: "GET",
483
+ signal: options?.signal
383
484
  });
384
485
  const blob = await response.blob();
385
486
  if (responseType === "blob")
@@ -388,48 +489,19 @@ class VaultFile {
388
489
  }
389
490
  }
390
491
 
391
- var __defProp = Object.defineProperty;
392
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
393
- var __publicField = (obj, key, value) => {
394
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
395
- return value;
396
- };
397
- class DataTokenAuthStrategy {
398
- constructor(dataToken) {
399
- __publicField(this, "dataToken");
400
- this.dataToken = dataToken;
401
- }
402
- getHeaders() {
403
- return new Headers({
404
- "x-data-token": this.dataToken
405
- });
406
- }
407
- }
408
- class APIKeyAuthStrategy {
409
- constructor(apiKey) {
410
- __publicField(this, "apiKey");
411
- this.apiKey = apiKey;
412
- }
413
- getHeaders() {
414
- return new Headers({
415
- Authorization: this.apiKey
416
- });
417
- }
418
- }
419
-
420
492
  function vaultClient(config) {
421
- function createFromContent(name, content) {
493
+ function createFromContent(name, content, options) {
422
494
  return VaultFile.fromContent({
423
495
  name,
424
496
  content,
425
497
  config
426
- });
498
+ }, { signal: options?.signal });
427
499
  }
428
- function createFromReference(reference) {
500
+ function createFromReference(reference, options) {
429
501
  return VaultFile.fromVaultReference({
430
502
  reference,
431
503
  config
432
- });
504
+ }, { signal: options?.signal });
433
505
  }
434
506
  return { createFromContent, createFromReference };
435
507
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/vault-sdk",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "license": "UNLICENSED",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,20 +18,16 @@
18
18
  "files": [
19
19
  "dist"
20
20
  ],
21
- "scripts": {
22
- "test": "vitest",
23
- "build": "unbuild",
24
- "lint": "eslint .",
25
- "lint:fix": "eslint . --fix",
26
- "check": "bun run lint && bun tsc --noEmit"
27
- },
28
21
  "dependencies": {
29
- "@meistrari/vault-shared": "0.0.2",
22
+ "file-type": "21.0.0",
23
+ "mime-types": "3.0.1",
30
24
  "ofetch": "1.4.1",
31
- "zod": "3.23.8"
25
+ "zod": "3.23.8",
26
+ "@meistrari/vault-shared": "0.0.3"
32
27
  },
33
28
  "devDependencies": {
34
29
  "@types/bun": "latest",
30
+ "@types/mime-types": "3.0.1",
35
31
  "msw": "2.6.8",
36
32
  "unbuild": "2.0.0",
37
33
  "vitest": "2.1.9"
@@ -41,5 +37,12 @@
41
37
  },
42
38
  "publishConfig": {
43
39
  "access": "public"
40
+ },
41
+ "scripts": {
42
+ "test": "vitest",
43
+ "build": "unbuild",
44
+ "lint": "eslint .",
45
+ "lint:fix": "eslint . --fix",
46
+ "check": "bun run lint && bun tsc --noEmit"
44
47
  }
45
- }
48
+ }