@meistrari/vault-sdk 1.7.0 → 1.8.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.
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { GetUploadUrlResponseV2, GetDownloadUrlResponse } from '@meistrari/vault-shared/schemas';
2
2
  import { fileTypeFromBlob } from '@meistrari/file-type';
3
3
  import { lookup } from 'mime-types';
4
+ import vaultUtils from '@meistrari/vault-shared/utils';
4
5
 
5
6
  var __defProp$2 = Object.defineProperty;
6
7
  var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -180,7 +181,7 @@ async function getFileHash(blob) {
180
181
  }
181
182
  async function detectFileMimeType(blob) {
182
183
  if (blob instanceof Blob && blob.type) {
183
- return blob.type;
184
+ return blob.type.split(";")[0].trim();
184
185
  }
185
186
  if ("name" in blob && typeof blob.name === "string") {
186
187
  const extension = blob.name.split(".").pop()?.toLowerCase();
@@ -217,9 +218,20 @@ async function detectFileMimeType(blob) {
217
218
  }
218
219
  return void 0;
219
220
  }
221
+ function basename(name, separator = "/") {
222
+ if (!name)
223
+ return void 0;
224
+ return name.substring(name.lastIndexOf(separator) + 1);
225
+ }
226
+ function getFileName(content) {
227
+ if ("name" in content && typeof content.name === "string") {
228
+ return basename(content.name);
229
+ }
230
+ return void 0;
231
+ }
220
232
 
221
233
  const name = "@meistrari/vault-sdk";
222
- const version = "1.7.0";
234
+ const version = "1.8.1";
223
235
  const license = "UNLICENSED";
224
236
  const repository = {
225
237
  type: "git",
@@ -288,9 +300,6 @@ var __publicField = (obj, key, value) => {
288
300
  return value;
289
301
  };
290
302
  const compatibilityDate = "2025-05-19";
291
- function removeVaultPrefix(url) {
292
- return url.replace("vault://", "");
293
- }
294
303
  function detectMimeTypeFromFilename(filename) {
295
304
  const extension = filename.split(".").pop()?.toLowerCase();
296
305
  if (extension) {
@@ -397,6 +406,7 @@ class VaultFile {
397
406
  * @param metadata - The metadata for creating a file
398
407
  * @param metadata.size - The size of the file
399
408
  * @param metadata.mimeType - The mime type of the file
409
+ * @param metadata.parentId - The ID of the parent file for hierarchical file relationships
400
410
  * @param options - The options for the request
401
411
  * @param options.signal - The signal to abort the request
402
412
  *
@@ -409,7 +419,9 @@ class VaultFile {
409
419
  method: "POST",
410
420
  path: `files`,
411
421
  body: JSON.stringify({
412
- ...metadata,
422
+ size: metadata.size,
423
+ mimeType: metadata.mimeType,
424
+ parentId: metadata.parentId,
413
425
  fileName: this.name,
414
426
  sha256sum: this.id ?? this.metadata?.id ?? (this.content ? await getFileHash(this.content) : void 0)
415
427
  }),
@@ -466,7 +478,7 @@ class VaultFile {
466
478
  const { reference, config: vaultConfig, download = false } = params;
467
479
  const config = resolveConfig(vaultConfig);
468
480
  const { vaultUrl, authStrategy } = config;
469
- const id = removeVaultPrefix(reference);
481
+ const id = vaultUtils.file.getFileIdFromVaultReference(reference);
470
482
  const url = new URL(`files/${id}`, config.vaultUrl);
471
483
  const response = await wrappedFetch(url, {
472
484
  method: "GET",
@@ -497,7 +509,9 @@ class VaultFile {
497
509
  * @param params.name - The name of the file
498
510
  * @param params.content - The content of the file
499
511
  * @param params.config - The configuration for the VaultFile
512
+ * @param params.mimeType - The MIME type of the file (optional)
500
513
  * @param params.upload - Whether to upload the file (default: false)
514
+ * @param params.parentId - The ID of the parent file for hierarchical file relationships
501
515
  * @param options - The options for the request
502
516
  * @param options.signal - The signal to abort the request
503
517
  *
@@ -532,13 +546,30 @@ class VaultFile {
532
546
  * upload: true
533
547
  * })
534
548
  * ```
549
+ *
550
+ * @example
551
+ * ```ts
552
+ * // Create a child file with a parent
553
+ * const file = new File(['content'], 'child.txt')
554
+ * const vaultFile = await VaultFile.fromContent({
555
+ * name: 'child.txt',
556
+ * content: file,
557
+ * parentId: 'parent-file-id',
558
+ * config: {
559
+ * vaultUrl,
560
+ * authStrategy,
561
+ * },
562
+ * upload: true
563
+ * })
564
+ * ```
535
565
  */
536
566
  static async fromContent(params, options) {
537
- const { name, content, config: vaultConfig, upload = false } = params;
567
+ const { content, config: vaultConfig, upload = false, parentId } = params;
568
+ const name = basename(params.name) ?? getFileName(content);
538
569
  const config = resolveConfig(vaultConfig);
539
570
  const { vaultUrl, authStrategy } = config;
540
571
  const sha256sum = await getFileHash(content);
541
- const mimeType = await detectFileMimeType(content);
572
+ const mimeType = params.mimeType ?? await detectFileMimeType(content);
542
573
  const size = content.size;
543
574
  const file = new VaultFile({
544
575
  content,
@@ -551,7 +582,8 @@ class VaultFile {
551
582
  });
552
583
  const createdFile = await file._createFile({
553
584
  size,
554
- mimeType
585
+ mimeType,
586
+ parentId
555
587
  }, { signal: options?.signal });
556
588
  if (upload) {
557
589
  await file.upload(file.content, createdFile.uploadUrl, { signal: options?.signal });
@@ -567,6 +599,7 @@ class VaultFile {
567
599
  * @param params.contentLength - The size of the content in bytes
568
600
  * @param params.config - The configuration for the VaultFile
569
601
  * @param params.contentType - The MIME type of the content (optional)
602
+ * @param params.parentId - The ID of the parent file for hierarchical file relationships
570
603
  * @param options - The options for the request
571
604
  * @param options.signal - The signal to abort the request
572
605
  *
@@ -589,25 +622,66 @@ class VaultFile {
589
622
  * contentType: file.type
590
623
  * })
591
624
  * ```
625
+ *
626
+ * @example
627
+ * ```ts
628
+ * // Create a child file with streaming
629
+ * const vaultFile = await VaultFile.fromStream({
630
+ * name: 'child-video.mp4',
631
+ * contentLength: 50 * 1024 * 1024,
632
+ * contentType: 'video/mp4',
633
+ * parentId: 'parent-file-id',
634
+ * config: { vaultUrl, authStrategy }
635
+ * })
636
+ * ```
592
637
  */
593
638
  static async fromStream(params, options) {
594
- const { name, contentLength, config: vaultConfig, contentType } = params;
639
+ const { contentLength, config: vaultConfig, contentType, parentId } = params;
640
+ const name = basename(params.name);
595
641
  const config = resolveConfig(vaultConfig);
596
642
  const file = new VaultFile({ config, name });
597
643
  await file._createFile({
598
644
  size: contentLength,
599
- mimeType: contentType || "application/octet-stream"
645
+ mimeType: contentType || "application/octet-stream",
646
+ parentId
600
647
  }, { signal: options?.signal });
601
648
  return file;
602
649
  }
603
650
  /**
604
- * Populates the metadata of the file instance.
605
- * @param options - The options for the request
606
- * @param options.signal - The signal to abort the request
651
+ * Fetches and populates the metadata fields for this VaultFile instance.
652
+ *
653
+ * This method retrieves the file's metadata from the vault server and updates the instance's
654
+ * metadata, name, and id properties. Useful when you have a VaultFile instance that was created
655
+ * without full metadata or when you need to refresh the metadata.
607
656
  *
608
- * @returns The file instance
657
+ * @param options - Additional options for the request
658
+ * @param options.signal - AbortSignal to cancel the request
659
+ *
660
+ * @returns The VaultFile instance with populated metadata (for method chaining)
609
661
  * @throws {Error} If the file ID is not set
610
662
  * @throws {FetchError} If the metadata fetch fails
663
+ *
664
+ * @example
665
+ * ```ts
666
+ * const vaultFile = await VaultFile.fromVaultReference({
667
+ * reference: 'vault://file-id',
668
+ * config: { vaultUrl, authStrategy }
669
+ * })
670
+ * await vaultFile.populateMetadata()
671
+ * console.log('File size:', vaultFile.metadata?.size)
672
+ * console.log('MIME type:', vaultFile.metadata?.mimeType)
673
+ * ```
674
+ *
675
+ * @example
676
+ * ```ts
677
+ * // Chain with other operations
678
+ * const file = await VaultFile.fromVaultReference({
679
+ * reference: 'vault://file-id',
680
+ * config: { vaultUrl, authStrategy }
681
+ * })
682
+ * await file.populateMetadata()
683
+ * const children = await file.getChildren()
684
+ * ```
611
685
  */
612
686
  async populateMetadata(options) {
613
687
  try {
@@ -620,10 +694,38 @@ class VaultFile {
620
694
  }
621
695
  }
622
696
  /**
623
- * Gets the vault reference for this file.
697
+ * Returns the vault URI reference for this file.
698
+ *
699
+ * The vault reference is a URI in the format `vault://{fileId}` that can be used to
700
+ * uniquely identify and retrieve this file from the vault. This reference can be stored
701
+ * in databases, passed to other services, or used with {@link VaultFile.fromVaultReference}
702
+ * to reconstruct a VaultFile instance.
624
703
  *
625
- * @returns The vault reference in the format `vault://{fileId}`
704
+ * @returns The vault reference string in the format `vault://{fileId}`
626
705
  * @throws {Error} If the file ID is not set
706
+ *
707
+ * @example
708
+ * ```ts
709
+ * const file = await VaultFile.fromContent({
710
+ * name: 'document.txt',
711
+ * content: new Blob(['content']),
712
+ * config: { vaultUrl, authStrategy },
713
+ * upload: true
714
+ * })
715
+ * const reference = file.getVaultReference()
716
+ * // Returns: "vault://abc123..."
717
+ * // Store this reference in your database for later retrieval
718
+ * ```
719
+ *
720
+ * @example
721
+ * ```ts
722
+ * // Retrieve a file later using the reference
723
+ * const storedReference = database.get('file_reference')
724
+ * const file = await VaultFile.fromVaultReference({
725
+ * reference: storedReference,
726
+ * config: { vaultUrl, authStrategy }
727
+ * })
728
+ * ```
627
729
  */
628
730
  getVaultReference() {
629
731
  if (!this.id) {
@@ -632,13 +734,37 @@ class VaultFile {
632
734
  return `vault://${this.id}`;
633
735
  }
634
736
  /**
635
- * Fetches the metadata of the file.
636
- * @param options - The options for the request
637
- * @param options.signal - The signal to abort the request
737
+ * Fetches the complete metadata for this file from the vault server.
638
738
  *
639
- * @returns The metadata of the file
739
+ * Retrieves detailed information about the file including size, MIME type, creation date,
740
+ * workspace ID, and other metadata fields. Unlike {@link populateMetadata}, this method
741
+ * returns the metadata without modifying the VaultFile instance.
742
+ *
743
+ * @param options - Additional options for the request
744
+ * @param options.signal - AbortSignal to cancel the request
745
+ *
746
+ * @returns A Promise that resolves to the FileMetadata object
640
747
  * @throws {Error} If the file ID is not set
641
748
  * @throws {FetchError} If the metadata fetch fails
749
+ *
750
+ * @example
751
+ * ```ts
752
+ * const vaultFile = await VaultFile.fromVaultReference({
753
+ * reference: 'vault://file-id',
754
+ * config: { vaultUrl, authStrategy }
755
+ * })
756
+ * const metadata = await vaultFile.getFileMetadata()
757
+ * console.log('File size:', metadata.size)
758
+ * console.log('Created at:', metadata.createdAt)
759
+ * console.log('Workspace:', metadata.workspaceId)
760
+ * ```
761
+ *
762
+ * @example
763
+ * ```ts
764
+ * // Use with abort signal
765
+ * const controller = new AbortController()
766
+ * const metadata = await vaultFile.getFileMetadata({ signal: controller.signal })
767
+ * ```
642
768
  */
643
769
  async getFileMetadata(options) {
644
770
  if (!this.id) {
@@ -652,14 +778,41 @@ class VaultFile {
652
778
  return response;
653
779
  }
654
780
  /**
655
- * Fetches a upload URL for the file.
656
- * @param options - The options for the request
657
- * @param options.signal - The signal to abort the request
658
- * @param options.expiresIn - The number of seconds the upload URL will be valid for
781
+ * Retrieves a presigned upload URL for uploading file content to the vault.
782
+ *
783
+ * This method returns a temporary URL that can be used to upload the file content directly
784
+ * to cloud storage (e.g., S3). The URL is cached and reused if still valid. If the file
785
+ * doesn't exist yet, it will be created automatically.
659
786
  *
660
- * @returns The upload URL for the file
787
+ * @param options - Additional options for the request
788
+ * @param options.signal - AbortSignal to cancel the request
789
+ * @param options.expiresIn - Number of seconds the upload URL should be valid for (default: server-defined)
790
+ *
791
+ * @returns A Promise that resolves to a URL object for uploading the file
661
792
  * @throws {Error} If the vault service returns an invalid response
662
793
  * @throws {FetchError} If the upload URL fetch fails
794
+ *
795
+ * @example
796
+ * ```ts
797
+ * const vaultFile = await VaultFile.fromContent({
798
+ * name: 'document.txt',
799
+ * content: new Blob(['content']),
800
+ * config: { vaultUrl, authStrategy }
801
+ * })
802
+ * const uploadUrl = await vaultFile.getUploadUrl()
803
+ * // Use the URL for custom upload logic
804
+ * await fetch(uploadUrl, {
805
+ * method: 'PUT',
806
+ * body: file,
807
+ * headers: { 'Content-Type': 'text/plain' }
808
+ * })
809
+ * ```
810
+ *
811
+ * @example
812
+ * ```ts
813
+ * // Request a longer-lived upload URL
814
+ * const uploadUrl = await vaultFile.getUploadUrl({ expiresIn: 7200 }) // 2 hours
815
+ * ```
663
816
  */
664
817
  async getUploadUrl(options) {
665
818
  if (this.lastUploadUrl && this.lastUploadUrl.expiresAt > /* @__PURE__ */ new Date()) {
@@ -686,15 +839,38 @@ class VaultFile {
686
839
  return this.lastUploadUrl.url;
687
840
  }
688
841
  /**
689
- * Fetches a download URL for the file.
690
- * @param options - The options for the request
691
- * @param options.signal - The signal to abort the request
692
- * @param options.expiresIn - The number of seconds the download URL will be valid for
842
+ * Retrieves a presigned download URL for accessing the file content.
843
+ *
844
+ * This method returns a temporary URL that can be used to download the file content directly
845
+ * from cloud storage (e.g., S3). The URL is cached and reused if still valid. This is useful
846
+ * for generating shareable links or implementing custom download logic.
693
847
  *
694
- * @returns The download URL for the file
848
+ * @param options - Additional options for the request
849
+ * @param options.signal - AbortSignal to cancel the request
850
+ * @param options.expiresIn - Number of seconds the download URL should be valid for (default: server-defined)
851
+ *
852
+ * @returns A Promise that resolves to a URL object for downloading the file
695
853
  * @throws {Error} If the vault service returns an invalid response
696
- * @throws {Error} If not file ID, name or content is set
854
+ * @throws {Error} If no file ID, name, or content is set
697
855
  * @throws {FetchError} If the download URL fetch fails
856
+ *
857
+ * @example
858
+ * ```ts
859
+ * const vaultFile = await VaultFile.fromVaultReference({
860
+ * reference: 'vault://file-id',
861
+ * config: { vaultUrl, authStrategy }
862
+ * })
863
+ * const downloadUrl = await vaultFile.getDownloadUrl()
864
+ * // Use the URL to download the file
865
+ * window.location.href = downloadUrl.toString()
866
+ * ```
867
+ *
868
+ * @example
869
+ * ```ts
870
+ * // Request a longer-lived download URL
871
+ * const downloadUrl = await vaultFile.getDownloadUrl({ expiresIn: 3600 }) // 1 hour
872
+ * // Share this URL with others
873
+ * ```
698
874
  */
699
875
  async getDownloadUrl(options) {
700
876
  if (this.lastDownloadUrl && this.lastDownloadUrl.expiresAt > /* @__PURE__ */ new Date()) {
@@ -714,30 +890,51 @@ class VaultFile {
714
890
  return this.lastDownloadUrl.url;
715
891
  }
716
892
  /**
717
- * Uploads a file to the vault.
893
+ * Uploads file content to the vault using a presigned URL.
894
+ *
895
+ * This method handles the actual file upload to cloud storage. If no file is provided,
896
+ * it uses the content from the VaultFile instance. If no upload URL is provided, one
897
+ * will be fetched automatically. The MIME type is detected automatically if not already set.
898
+ *
899
+ * @param file - The Blob or File to upload. If not provided, uses the instance's content property
900
+ * @param url - A presigned upload URL. If not provided, will be fetched via {@link getUploadUrl}
901
+ * @param options - Additional options for the request
902
+ * @param options.signal - AbortSignal to cancel the upload
903
+ *
904
+ * @throws {Error} If no file content is available (neither provided nor in the instance)
905
+ * @throws {FetchError} If the upload request fails
906
+ * @returns A Promise that resolves when the upload completes successfully
718
907
  *
719
908
  * @example
720
909
  * ```ts
910
+ * // Simple upload using instance content
721
911
  * const file = new File(['content'], 'document.txt')
722
912
  * const vaultFile = await VaultFile.fromContent({
723
913
  * name: 'document.txt',
724
914
  * content: file,
725
- * config: {
726
- * vaultUrl,
727
- * authStrategy,
728
- * },
915
+ * config: { vaultUrl, authStrategy }
729
916
  * })
730
- * await vaultFile.upload(file)
917
+ * await vaultFile.upload()
731
918
  * ```
732
919
  *
733
- * @param file - The file to upload to the vault. If not provided, the file content will be taken from the `content` property.
734
- * @param url - The URL to upload the file to. If not provided, the upload URL will be fetched from the vault.
735
- * @param options - The options for the request
736
- * @param options.signal - The signal to abort the request
920
+ * @example
921
+ * ```ts
922
+ * // Upload different content than what's in the instance
923
+ * const vaultFile = await VaultFile.fromContent({
924
+ * name: 'document.txt',
925
+ * content: new Blob(['original']),
926
+ * config: { vaultUrl, authStrategy }
927
+ * })
928
+ * const newContent = new Blob(['updated content'])
929
+ * await vaultFile.upload(newContent)
930
+ * ```
737
931
  *
738
- * @throws {FetchError} If the upload fails
739
- * @throws {Error} If the file content is not set and no file is provided
740
- * @returns Promise that resolves when upload is complete
932
+ * @example
933
+ * ```ts
934
+ * // Upload with custom URL (advanced usage)
935
+ * const uploadUrl = await vaultFile.getUploadUrl({ expiresIn: 3600 })
936
+ * await vaultFile.upload(file, uploadUrl.toString())
937
+ * ```
741
938
  */
742
939
  async upload(file, url, options) {
743
940
  const content = file ?? this.content;
@@ -768,31 +965,60 @@ class VaultFile {
768
965
  return await blobToBase64(blob);
769
966
  }
770
967
  /**
771
- * Downloads a file from the vault as a stream for memory-efficient processing.
968
+ * Downloads the file as a ReadableStream for memory-efficient processing of large files.
772
969
  *
773
- * @param options - The options for the request
774
- * @param options.signal - The signal to abort the request
970
+ * This method is ideal for handling large files without loading the entire content into memory.
971
+ * The stream can be processed chunk-by-chunk, piped to other streams, or saved incrementally.
972
+ * This is the recommended approach for files larger than a few megabytes.
775
973
  *
776
- * @returns A ReadableStream that yields chunks of the file data
974
+ * @param options - Additional options for the request
975
+ * @param options.signal - AbortSignal to cancel the download
976
+ *
977
+ * @returns A Promise resolving to a ReadableStream that yields Uint8Array chunks
978
+ * @throws {Error} If the response body is not readable
979
+ * @throws {FetchError} If the download fails
777
980
  *
778
981
  * @example
779
982
  * ```ts
780
- * const vaultFile = await VaultFile.fromVaultReference('vault://1234567890', { vaultUrl, authStrategy })
983
+ * // Process large file chunk by chunk
984
+ * const vaultFile = await VaultFile.fromVaultReference({
985
+ * reference: 'vault://large-video-id',
986
+ * config: { vaultUrl, authStrategy }
987
+ * })
781
988
  * const stream = await vaultFile.downloadStream()
782
989
  *
783
- * // Process the stream chunk by chunk
784
990
  * const reader = stream.getReader()
991
+ * let bytesReceived = 0
785
992
  * try {
786
993
  * while (true) {
787
994
  * const { done, value } = await reader.read()
788
995
  * if (done) break
789
- * // Process the chunk (Uint8Array)
790
- * console.log('Received chunk of size:', value.length)
996
+ * bytesReceived += value.length
997
+ * console.log(`Downloaded ${bytesReceived} bytes...`)
998
+ * // Process chunk (value is Uint8Array)
791
999
  * }
792
1000
  * } finally {
793
1001
  * reader.releaseLock()
794
1002
  * }
795
1003
  * ```
1004
+ *
1005
+ * @example
1006
+ * ```ts
1007
+ * // Pipe stream to a writable destination
1008
+ * const stream = await vaultFile.downloadStream()
1009
+ * const fileHandle = await navigator.storage.getDirectory()
1010
+ * .then(dir => dir.getFileHandle('output.dat', { create: true }))
1011
+ * const writable = await fileHandle.createWritable()
1012
+ * await stream.pipeTo(writable)
1013
+ * ```
1014
+ *
1015
+ * @example
1016
+ * ```ts
1017
+ * // Use with abort signal
1018
+ * const controller = new AbortController()
1019
+ * const stream = await vaultFile.downloadStream({ signal: controller.signal })
1020
+ * // Later: controller.abort()
1021
+ * ```
796
1022
  */
797
1023
  async downloadStream(options) {
798
1024
  const downloadUrl = await this.getDownloadUrl({ signal: options?.signal });
@@ -806,32 +1032,64 @@ class VaultFile {
806
1032
  return response.body;
807
1033
  }
808
1034
  /**
809
- * Uploads a file to the vault using a stream for memory-efficient processing.
1035
+ * Uploads file content using a ReadableStream for memory-efficient processing of large files.
810
1036
  *
811
- * @param stream - The readable stream of file data to upload
812
- * @param options - The options for the request
813
- * @param options.signal - The signal to abort the request
814
- * @param options.contentLength - The total size of the content (required for S3 uploads)
815
- * @param options.contentType - The MIME type of the content (will be detected if not provided)
1037
+ * This method is ideal for uploading large files without loading the entire content into memory.
1038
+ * The stream is sent directly to cloud storage, making it perfect for files larger than a few
1039
+ * megabytes. Note: When using Bun, the stream is buffered due to implementation limitations.
1040
+ *
1041
+ * @param stream - A ReadableStream of Uint8Array chunks containing the file data
1042
+ * @param options - Required options for the upload
1043
+ * @param options.contentLength - The total size of the content in bytes (required for S3 uploads)
1044
+ * @param options.contentType - The MIME type of the content (auto-detected from filename if not provided)
1045
+ * @param options.signal - AbortSignal to cancel the upload
816
1046
  *
817
- * @throws {Error} If contentLength is not provided
1047
+ * @throws {Error} If contentLength is not provided or is negative
818
1048
  * @throws {FetchError} If the upload fails
819
- * @returns Promise that resolves when upload is complete
1049
+ * @returns A Promise that resolves when the upload completes successfully
820
1050
  *
821
1051
  * @example
822
1052
  * ```ts
823
- * const file = new File(['content'], 'document.txt')
824
- * const vaultFile = await VaultFile.fromStream('document.txt', file.size, {
1053
+ * // Upload a large file using streaming
1054
+ * const file = new File(['large content'], 'video.mp4')
1055
+ * const vaultFile = await VaultFile.fromStream({
1056
+ * name: 'video.mp4',
1057
+ * contentLength: file.size,
825
1058
  * contentType: file.type,
826
1059
  * config: { vaultUrl, authStrategy }
827
1060
  * })
828
1061
  *
829
- * // Upload using the stream directly
830
1062
  * const stream = file.stream()
831
1063
  * await vaultFile.uploadStream(stream, {
832
1064
  * contentLength: file.size,
833
1065
  * contentType: file.type
834
1066
  * })
1067
+ * console.log('Upload complete!')
1068
+ * ```
1069
+ *
1070
+ * @example
1071
+ * ```ts
1072
+ * // Upload with progress tracking
1073
+ * const vaultFile = await VaultFile.fromStream({
1074
+ * name: 'document.pdf',
1075
+ * contentLength: fileSize,
1076
+ * config: { vaultUrl, authStrategy }
1077
+ * })
1078
+ *
1079
+ * // Create a transform stream to track progress
1080
+ * let uploaded = 0
1081
+ * const progressStream = new TransformStream({
1082
+ * transform(chunk, controller) {
1083
+ * uploaded += chunk.length
1084
+ * console.log(`Uploaded ${uploaded}/${fileSize} bytes`)
1085
+ * controller.enqueue(chunk)
1086
+ * }
1087
+ * })
1088
+ *
1089
+ * await vaultFile.uploadStream(
1090
+ * originalStream.pipeThrough(progressStream),
1091
+ * { contentLength: fileSize }
1092
+ * )
835
1093
  * ```
836
1094
  */
837
1095
  async uploadStream(stream, options) {
@@ -872,10 +1130,48 @@ class VaultFile {
872
1130
  });
873
1131
  }
874
1132
  /**
875
- * Deletes the file from the vault.
876
- * @param options - The options for the request
877
- * @param options.signal - The signal to abort the request
1133
+ * Permanently deletes this file from the vault.
1134
+ *
1135
+ * This operation removes the file metadata and content from the vault. This action cannot be
1136
+ * undone. Any vault references or permalinks to this file will become invalid after deletion.
1137
+ * Note: This does not automatically delete child files in hierarchical relationships.
1138
+ *
1139
+ * @param options - Additional options for the request
1140
+ * @param options.signal - AbortSignal to cancel the request
1141
+ *
1142
+ * @throws {Error} If the file ID is not set
1143
+ * @throws {FetchError} If the deletion request fails
1144
+ * @returns A Promise that resolves when the deletion completes successfully
1145
+ *
1146
+ * @example
1147
+ * ```ts
1148
+ * // Delete a file
1149
+ * const vaultFile = await VaultFile.fromVaultReference({
1150
+ * reference: 'vault://file-id',
1151
+ * config: { vaultUrl, authStrategy }
1152
+ * })
1153
+ * await vaultFile.delete()
1154
+ * console.log('File deleted successfully')
1155
+ * ```
1156
+ *
1157
+ * @example
1158
+ * ```ts
1159
+ * // Delete with confirmation
1160
+ * const vaultFile = await VaultFile.fromVaultReference({
1161
+ * reference: 'vault://file-id',
1162
+ * config: { vaultUrl, authStrategy }
1163
+ * })
1164
+ * if (confirm('Are you sure you want to delete this file?')) {
1165
+ * await vaultFile.delete()
1166
+ * }
1167
+ * ```
878
1168
  *
1169
+ * @example
1170
+ * ```ts
1171
+ * // Delete with abort signal
1172
+ * const controller = new AbortController()
1173
+ * await vaultFile.delete({ signal: controller.signal })
1174
+ * ```
879
1175
  */
880
1176
  async delete(options) {
881
1177
  if (!this.id) {
@@ -888,13 +1184,51 @@ class VaultFile {
888
1184
  });
889
1185
  }
890
1186
  /**
891
- * Creates a permalink for the file.
892
- * @param params - The parameters for the permalink
893
- * @param params.expiresIn - The number of seconds the permalink will be valid for
894
- * @param options - The options for the request
895
- * @param options.signal - The signal to abort the request
1187
+ * Creates a new shareable permalink for this file.
1188
+ *
1189
+ * A permalink is a public URL that allows anyone with the link to access the file without
1190
+ * authentication. The permalink can optionally expire after a specified duration. This is
1191
+ * useful for sharing files with external users or embedding files in public content.
1192
+ *
1193
+ * @param params - Optional parameters for permalink creation
1194
+ * @param params.expiresIn - Number of seconds until the permalink expires (optional, defaults to server setting)
1195
+ * @param options - Additional options for the request
1196
+ * @param options.signal - AbortSignal to cancel the request
1197
+ *
1198
+ * @throws {Error} If the file ID is not set
1199
+ * @throws {Error} If the workspace ID is not available (call {@link populateMetadata} first)
1200
+ * @throws {FetchError} If the permalink creation fails
1201
+ * @returns A Promise that resolves to a Permalink instance
1202
+ *
1203
+ * @example
1204
+ * ```ts
1205
+ * // Create a permanent permalink
1206
+ * const vaultFile = await VaultFile.fromVaultReference({
1207
+ * reference: 'vault://file-id',
1208
+ * config: { vaultUrl, authStrategy }
1209
+ * })
1210
+ * await vaultFile.populateMetadata() // Required to get workspace ID
1211
+ * const permalink = await vaultFile.createPermalink()
1212
+ * console.log('Share this link:', permalink.url)
1213
+ * ```
1214
+ *
1215
+ * @example
1216
+ * ```ts
1217
+ * // Create a permalink that expires in 24 hours
1218
+ * await vaultFile.populateMetadata()
1219
+ * const permalink = await vaultFile.createPermalink({
1220
+ * expiresIn: 24 * 60 * 60 // 24 hours in seconds
1221
+ * })
1222
+ * console.log('Link expires at:', permalink.expiresAt)
1223
+ * ```
896
1224
  *
897
- * @returns The permalink for the file
1225
+ * @example
1226
+ * ```ts
1227
+ * // Create a short-lived sharing link (1 hour)
1228
+ * await vaultFile.populateMetadata()
1229
+ * const permalink = await vaultFile.createPermalink({ expiresIn: 3600 })
1230
+ * // Send permalink.url to user
1231
+ * ```
898
1232
  */
899
1233
  async createPermalink(params = {}, options) {
900
1234
  if (!this.id) {
@@ -910,11 +1244,51 @@ class VaultFile {
910
1244
  }, options);
911
1245
  }
912
1246
  /**
913
- * Gets a list of the valid permalinks for the file.
1247
+ * Retrieves all active permalinks associated with this file.
1248
+ *
1249
+ * This method fetches all permalinks that have been created for this file and are still
1250
+ * valid (not expired or deleted). Each permalink is returned as a Permalink instance
1251
+ * with full details including URL, expiration date, and other metadata.
1252
+ *
914
1253
  * @param options - Additional options for the request
915
- * @param options.signal - Abort signal
1254
+ * @param options.signal - AbortSignal to cancel the request
1255
+ *
1256
+ * @throws {Error} If the file ID is not set
1257
+ * @throws {FetchError} If the request to fetch permalinks fails
1258
+ * @returns A Promise that resolves to an array of Permalink instances
916
1259
  *
917
- * @returns The permalinks for the file
1260
+ * @example
1261
+ * ```ts
1262
+ * // List all permalinks for a file
1263
+ * const vaultFile = await VaultFile.fromVaultReference({
1264
+ * reference: 'vault://file-id',
1265
+ * config: { vaultUrl, authStrategy }
1266
+ * })
1267
+ * const permalinks = await vaultFile.getPermalinks()
1268
+ * permalinks.forEach(permalink => {
1269
+ * console.log('URL:', permalink.url)
1270
+ * console.log('Expires:', permalink.expiresAt || 'Never')
1271
+ * })
1272
+ * ```
1273
+ *
1274
+ * @example
1275
+ * ```ts
1276
+ * // Check if any permalinks exist
1277
+ * const permalinks = await vaultFile.getPermalinks()
1278
+ * if (permalinks.length > 0) {
1279
+ * console.log(`This file has ${permalinks.length} active permalink(s)`)
1280
+ * } else {
1281
+ * console.log('No active permalinks')
1282
+ * }
1283
+ * ```
1284
+ *
1285
+ * @example
1286
+ * ```ts
1287
+ * // Find non-expiring permalinks
1288
+ * const permalinks = await vaultFile.getPermalinks()
1289
+ * const permanent = permalinks.filter(p => !p.expiresAt)
1290
+ * console.log(`Found ${permanent.length} permanent links`)
1291
+ * ```
918
1292
  */
919
1293
  async getPermalinks(options) {
920
1294
  if (!this.id) {
@@ -927,6 +1301,273 @@ class VaultFile {
927
1301
  });
928
1302
  return permalinks.map((data) => new Permalink(this.config, data));
929
1303
  }
1304
+ /**
1305
+ * Retrieves all child files in the hierarchical file relationship.
1306
+ *
1307
+ * This method queries files that have this file set as their parent (via parentId).
1308
+ * Returns an empty array if the file has no children or if the file doesn't exist.
1309
+ * The returned children are VaultFile instances with populated metadata but no content.
1310
+ *
1311
+ * @param params - Optional parameters for filtering and request control
1312
+ * @param params.mimeType - Filter children by MIME type (e.g., 'image/png', 'application/pdf')
1313
+ * @param params.includeDeleted - Whether to include deleted child files (default: false)
1314
+ * @param params.limit - Maximum number of children to return (default: 100)
1315
+ * @param params.offset - Number of children to skip (default: 0)
1316
+ * @param options - Additional options for the request
1317
+ * @param options.signal - AbortSignal to cancel the request
1318
+ *
1319
+ * @returns An array of VaultFile instances representing the child files
1320
+ * @throws {Error} If the file ID is not set
1321
+ * @throws {FetchError} If the request to fetch children fails
1322
+ *
1323
+ * @example
1324
+ * ```ts
1325
+ * // Get all children of a file
1326
+ * const parent = await VaultFile.fromVaultReference({
1327
+ * reference: 'vault://parent-id',
1328
+ * config: { vaultUrl, authStrategy }
1329
+ * })
1330
+ * const children = await parent.getChildren()
1331
+ * console.log(`Found ${children.length} child files`)
1332
+ * ```
1333
+ *
1334
+ * @example
1335
+ * ```ts
1336
+ * // Get only image children
1337
+ * const parent = await VaultFile.fromVaultReference({
1338
+ * reference: 'vault://parent-id',
1339
+ * config: { vaultUrl, authStrategy }
1340
+ * })
1341
+ * const imageChildren = await parent.getChildren({ mimeType: 'image/png' })
1342
+ * ```
1343
+ *
1344
+ * @example
1345
+ * ```ts
1346
+ * // Get children with pagination
1347
+ * const parent = await VaultFile.fromVaultReference({
1348
+ * reference: 'vault://parent-id',
1349
+ * config: { vaultUrl, authStrategy }
1350
+ * })
1351
+ * const firstPage = await parent.getChildren({ limit: 10, offset: 0 })
1352
+ * const secondPage = await parent.getChildren({ limit: 10, offset: 10 })
1353
+ * ```
1354
+ *
1355
+ * @example
1356
+ * ```ts
1357
+ * // Use with abort signal for cancellable requests
1358
+ * const controller = new AbortController()
1359
+ * const children = await parent.getChildren({}, { signal: controller.signal })
1360
+ * // Later: controller.abort()
1361
+ * ```
1362
+ */
1363
+ async getChildren(params = {}, options) {
1364
+ if (!this.id) {
1365
+ throw new Error("File ID is not set");
1366
+ }
1367
+ const query = {};
1368
+ if (params.mimeType) {
1369
+ query.mimeType = params.mimeType;
1370
+ }
1371
+ if (params.includeDeleted) {
1372
+ query.includeDeleted = params.includeDeleted ? "true" : "false";
1373
+ }
1374
+ if (params.limit !== void 0) {
1375
+ query.limit = params.limit.toString();
1376
+ }
1377
+ if (params.offset !== void 0) {
1378
+ query.offset = params.offset.toString();
1379
+ }
1380
+ const response = await this._fetch({
1381
+ method: "GET",
1382
+ path: `files/${this.id}/children`,
1383
+ signal: options?.signal,
1384
+ query
1385
+ });
1386
+ return response.files.map((data) => new VaultFile({
1387
+ config: this.config,
1388
+ id: data.id,
1389
+ name: data.originalFileName ?? void 0,
1390
+ metadata: data
1391
+ }));
1392
+ }
1393
+ /**
1394
+ * Iterates through all child files with automatic pagination.
1395
+ *
1396
+ * This method returns an async iterator that automatically fetches pages of children
1397
+ * as you iterate through them. This is useful when you have a large number of children
1398
+ * and want to process them without loading all of them into memory at once.
1399
+ *
1400
+ * @param params - Optional parameters for filtering and pagination
1401
+ * @param params.pageSize - Number of children to fetch per page (default: 100)
1402
+ * @param params.mimeType - Filter children by MIME type (e.g., 'image/png', 'application/pdf')
1403
+ * @param params.includeDeleted - Whether to include deleted child files (default: false)
1404
+ * @param options - Additional options for the request
1405
+ * @param options.signal - AbortSignal to cancel the iteration
1406
+ *
1407
+ * @returns An async iterator that yields VaultFile instances
1408
+ * @throws {Error} If the file ID is not set
1409
+ * @throws {FetchError} If the request to fetch children fails
1410
+ *
1411
+ * @example
1412
+ * ```ts
1413
+ * // Iterate through all children
1414
+ * const parent = await VaultFile.fromVaultReference({
1415
+ * reference: 'vault://parent-id',
1416
+ * config: { vaultUrl, authStrategy }
1417
+ * })
1418
+ *
1419
+ * for await (const child of parent.iterateChildren()) {
1420
+ * console.log(`Processing child: ${child.name}`)
1421
+ * }
1422
+ * ```
1423
+ *
1424
+ * @example
1425
+ * ```ts
1426
+ * // Iterate with filters and custom page size
1427
+ * for await (const child of parent.iterateChildren({
1428
+ * pageSize: 50,
1429
+ * mimeType: 'image/png'
1430
+ * })) {
1431
+ * console.log(`Processing image: ${child.name}`)
1432
+ * }
1433
+ * ```
1434
+ *
1435
+ * @example
1436
+ * ```ts
1437
+ * // Use with abort signal
1438
+ * const controller = new AbortController()
1439
+ * try {
1440
+ * for await (const child of parent.iterateChildren({}, { signal: controller.signal })) {
1441
+ * console.log(child.name)
1442
+ * // Can abort iteration at any time
1443
+ * if (someCondition) {
1444
+ * controller.abort()
1445
+ * }
1446
+ * }
1447
+ * } catch (error) {
1448
+ * if (error.name === 'AbortError') {
1449
+ * console.log('Iteration cancelled')
1450
+ * }
1451
+ * }
1452
+ * ```
1453
+ */
1454
+ async *iterateChildren(params = {}, options) {
1455
+ if (!this.id) {
1456
+ throw new Error("File ID is not set");
1457
+ }
1458
+ const pageSize = params.pageSize ?? 100;
1459
+ let offset = 0;
1460
+ let hasMore = true;
1461
+ while (hasMore) {
1462
+ const children = await this.getChildren({
1463
+ limit: pageSize,
1464
+ offset,
1465
+ mimeType: params.mimeType,
1466
+ includeDeleted: params.includeDeleted
1467
+ }, options);
1468
+ for (const child of children) {
1469
+ yield child;
1470
+ }
1471
+ hasMore = children.length === pageSize;
1472
+ offset += children.length;
1473
+ if (children.length === 0) {
1474
+ hasMore = false;
1475
+ }
1476
+ }
1477
+ }
1478
+ /**
1479
+ * Creates a child file from content with this file as the parent.
1480
+ *
1481
+ * This is a convenience method that wraps {@link VaultFile.fromContent} and automatically
1482
+ * sets the current file as the parent. All parameters and behavior are identical to
1483
+ * {@link VaultFile.fromContent}, except parentId is automatically provided.
1484
+ *
1485
+ * @param params - Parameters for creating the child file (same as fromContent, minus parentId)
1486
+ * @param options - Additional options for the request
1487
+ *
1488
+ * @returns A new VaultFile instance representing the child file
1489
+ * @throws {Error} If the parent file ID is not set
1490
+ * @throws {FetchError} If the child file creation fails
1491
+ *
1492
+ * @example
1493
+ * ```ts
1494
+ * // Create a child file
1495
+ * const parent = await VaultFile.fromContent({
1496
+ * name: 'parent.txt',
1497
+ * content: new Blob(['parent content']),
1498
+ * config: { vaultUrl, authStrategy },
1499
+ * upload: true
1500
+ * })
1501
+ * const child = await parent.createChildFromContent({
1502
+ * name: 'child.txt',
1503
+ * content: new Blob(['child content']),
1504
+ * upload: true
1505
+ * })
1506
+ * console.log('Child created with parent:', child.metadata?.parentId)
1507
+ * ```
1508
+ */
1509
+ async createChildFromContent(params, options) {
1510
+ if (!this.id) {
1511
+ throw new Error("Parent file ID is not set");
1512
+ }
1513
+ return VaultFile.fromContent(
1514
+ {
1515
+ ...params,
1516
+ config: this.config,
1517
+ parentId: this.id
1518
+ },
1519
+ options
1520
+ );
1521
+ }
1522
+ /**
1523
+ * Creates a child file from a stream with this file as the parent.
1524
+ *
1525
+ * This is a convenience method that wraps {@link VaultFile.fromStream} and automatically
1526
+ * sets the current file as the parent. All parameters and behavior are identical to
1527
+ * {@link VaultFile.fromStream}, except parentId is automatically provided.
1528
+ *
1529
+ * @param params - Parameters for creating the child file (same as fromStream, minus parentId)
1530
+ * @param options - Additional options for the request
1531
+ *
1532
+ * @returns A new VaultFile instance ready for streaming upload as a child
1533
+ * @throws {Error} If the parent file ID is not set
1534
+ * @throws {FetchError} If the child file creation fails
1535
+ *
1536
+ * @example
1537
+ * ```ts
1538
+ * // Create a child file for streaming
1539
+ * const parent = await VaultFile.fromContent({
1540
+ * name: 'parent.txt',
1541
+ * content: new Blob(['parent']),
1542
+ * config: { vaultUrl, authStrategy },
1543
+ * upload: true
1544
+ * })
1545
+ * const child = await parent.createChildFromStream({
1546
+ * name: 'large-child.mp4',
1547
+ * contentLength: 100 * 1024 * 1024,
1548
+ * contentType: 'video/mp4'
1549
+ * })
1550
+ * // Upload the stream
1551
+ * const stream = file.stream()
1552
+ * await child.uploadStream(stream, {
1553
+ * contentLength: file.size,
1554
+ * contentType: file.type
1555
+ * })
1556
+ * ```
1557
+ */
1558
+ async createChildFromStream(params, options) {
1559
+ if (!this.id) {
1560
+ throw new Error("Parent file ID is not set");
1561
+ }
1562
+ return VaultFile.fromStream(
1563
+ {
1564
+ ...params,
1565
+ config: this.config,
1566
+ parentId: this.id
1567
+ },
1568
+ options
1569
+ );
1570
+ }
930
1571
  }
931
1572
 
932
1573
  function isS3UrlExpired(url) {
@@ -978,7 +1619,7 @@ const URL_STRATEGIES = [
978
1619
  ];
979
1620
  function extractVaultFileIdFromS3Url(url) {
980
1621
  if (isVaultReference(url))
981
- return url.replace("vault://", "");
1622
+ return vaultUtils.file.getFileIdFromVaultReference(url);
982
1623
  try {
983
1624
  if (!isVaultFileS3Url(url))
984
1625
  return null;
@@ -1012,12 +1653,18 @@ function vaultClient(vaultConfig) {
1012
1653
  const name2 = nameOrContent;
1013
1654
  const content2 = contentOrNameOrOptions;
1014
1655
  const signal2 = options?.signal;
1015
- return VaultFile.fromContent({ content: content2, name: name2, config }, { signal: signal2 });
1656
+ const parentId2 = options?.parentId;
1657
+ const mimeType2 = options?.mimeType;
1658
+ const upload2 = options?.upload;
1659
+ return VaultFile.fromContent({ content: content2, name: name2, config, parentId: parentId2, mimeType: mimeType2, upload: upload2 }, { signal: signal2 });
1016
1660
  }
1017
1661
  const content = nameOrContent;
1018
1662
  const name = typeof contentOrNameOrOptions === "string" ? contentOrNameOrOptions : void 0;
1019
1663
  const signal = isOptionsObject(contentOrNameOrOptions) ? contentOrNameOrOptions.signal : options?.signal;
1020
- return VaultFile.fromContent({ content, name, config }, { signal });
1664
+ const parentId = isOptionsObject(contentOrNameOrOptions) ? contentOrNameOrOptions.parentId : options?.parentId;
1665
+ const mimeType = isOptionsObject(contentOrNameOrOptions) ? contentOrNameOrOptions.mimeType : options?.mimeType;
1666
+ const upload = isOptionsObject(contentOrNameOrOptions) ? contentOrNameOrOptions.upload : options?.upload;
1667
+ return VaultFile.fromContent({ content, name, config, parentId, mimeType, upload }, { signal });
1021
1668
  }
1022
1669
  function createFromReference(reference, options) {
1023
1670
  return VaultFile.fromVaultReference({
@@ -1030,7 +1677,8 @@ function vaultClient(vaultConfig) {
1030
1677
  name,
1031
1678
  contentLength,
1032
1679
  config,
1033
- contentType: options?.contentType
1680
+ contentType: options?.contentType,
1681
+ parentId: options?.parentId
1034
1682
  }, { signal: options?.signal });
1035
1683
  }
1036
1684
  return { createFromContent, createFromReference, createFromStream };