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