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