UNPKG

@zerochain/sdk

Version:

The Züs JS SDK is a JavaScript client library that provides a convenient interface for interacting with the Züs Network. It allows developers to perform various operations such as creating and managing allocations, uploading and downloading files, executi

991 lines (925 loc) 27.1 kB
// TODO: not used in webapps: deleteFile, multiDownload, multiUpload, downloadBlocks, updateBlobberSettings, getRemoteFileMap (deprecated), getContainers, updateContainer, searchContainer, repairAllocation, checkAllocStatus, createWorkers, getFileMetaByName, downloadDirectory. // TODO: GoSDK methods related to "player" proxy are deprecated import { errorOut } from '@/sdk/utils/misc' import { getBridge, globalCtx } from '@/setup/createWasm/bridge' import { readChunk } from '@/setup/createWasm/createProxy/sdkProxy' import { getWasm } from '@/setup/wasm' import type { Blobber, FileRefByName, GolangFileRefByName, ListResult, } from '@/types/blobber' import type { ActiveWallet, NetworkDomain, Transaction } from '@/types/wallet' /** Deletes a file from an allocation. Only the owner of the allocation can delete a file. * * To perform multiple deletions in a single call, use the `multiOperation` method instead. */ export const deleteFile = async ({ wallet, domain, allocationId, remotePath, }: { domain: NetworkDomain wallet: ActiveWallet allocationId: string /** remote path of the file to be deleted*/ remotePath: string }): Promise<{ commandSuccess: boolean; error: string }> => { const goWasm = await getWasm({ domain, wallet }) try { const response = await goWasm.sdk.delete(allocationId, remotePath) if (response.error) throw new Error(response.error) return response } catch (e) { throw errorOut('deleteFile', e) } } /** Generates an `authTicket` that provides authorization to the holder to the specified file on the remotepath. */ export const share = async ({ wallet, domain, allocationId, remotePath, clientId = '', encryptionPublicKey = '', expiration = 0, revoke = false, availableAfter = '', }: { domain: NetworkDomain wallet: ActiveWallet allocationId: string /** Remote path of the file to be shared */ remotePath: string /** Client ID / wallet ID of the recipient (for public sharing) */ clientId?: string /** Encryption public key of the recipient (for private sharing) */ encryptionPublicKey?: string /** * Expiration time of the auth ticket (in Unix timestamp seconds. e.g. `1647858200`) * @default 0 (no expiration) */ expiration?: number /** Whether to revoke the share. Only applicable for private sharing */ revoke?: boolean /** Time after which the share becomes available */ availableAfter?: string }): Promise<string> => { const goWasm = await getWasm({ domain, wallet }) try { const authTicket = await goWasm.sdk.share( allocationId, remotePath, clientId, encryptionPublicKey, expiration, revoke, availableAfter ) if (!authTicket) throw new Error('authTicket is undefined') return authTicket } catch (e) { throw errorOut('shareFile', e) } } /** Options for a single file download, usually as part of a multi-download request. */ type MultiDownloadOption = { /** Remote path of the file to be downloaded */ remotePath: string /** Local path where the file should be stored (optional) */ localPath?: string /** Download operation type */ downloadType: 'file' | 'thumbnail' /** Number of blocks to download per request @default 100 */ numBlocks?: number /** @optional Required only for file download with an auth ticket */ remoteFileName: string /** @optional Lookup hash for remote file, required for auth ticket downloads */ remoteLookupHash?: string /** Whether to download the file directly to disk - This uses FileSytem API. This is not supported on Safari browser. Check: https://developer.mozilla.org/en-US/docs/Web/API/Window/showSaveFilePicker */ downloadToDisk: boolean /** Suggested name for the file when downloading to disk (optional) */ suggestedName?: string } /** Response format for each downloaded file in a multi-download operation */ type DownloadCommandResponse = { commandSuccess: boolean error?: string /** Name of the downloaded file */ fileName?: string /** Blob URL of the downloaded file */ url?: string } /** Downloads multiple files in parallel in a batch. This method supports downloading files directly to disk (if supported by the browser) and provides progress updates via a callback function. */ export const multiDownload = async ({ wallet, domain, allocationId, multiDownloadOptions, authTicket = '', callback, }: { domain: NetworkDomain wallet: ActiveWallet allocationId: string /** @optional Required only for download of a shared files (non-owner) */ authTicket?: string /** Array of download options for each file */ multiDownloadOptions: MultiDownloadOption[] /** Callback function will be invoked with progress updates */ callback?: ( totalBytes: number, completedBytes: number, fileName: string, blobURL: string, error?: string ) => void }): Promise<DownloadCommandResponse[]> => { const multiDlOptions = multiDownloadOptions.map(option => { let downloadToDisk = option.downloadToDisk if (downloadToDisk) { const isSupported = 'showSaveFilePicker' in window // ref https://0chain.slack.com/archives/C05JA2A662X/p1704447229385239?thread_ts=1704223167.599399&cid=C05JA2A662X downloadToDisk = isSupported } return { ...option, numBlocks: option.numBlocks || 100, downloadOp: option.downloadType === 'file' ? 1 : 2, downloadToDisk, } }) const goWasm = await getWasm({ domain, wallet }) let callbackFuncName = '' if (callback) { callbackFuncName = `multiDownloadCallback_${Date.now()}` window[callbackFuncName] = callback } try { const jsonData = (await goWasm.sdk.multiDownload( allocationId, JSON.stringify(multiDlOptions), authTicket, callbackFuncName )) as string const response = JSON.parse(jsonData) return response } catch (e) { throw errorOut('multiDownload', e) } finally { // TODO: check if this is to be avoided or not if (callbackFuncName) delete window[callbackFuncName] } } /** Sets the upload mode for modifying the upload speed and CPU usage . Possible upload modes: * - 0 = Slow uploads (Consumes less CPU & Memory) * - 1 = Standard (Default) * - 2 = High Speed uploads (Consumes more CPU & Memory) */ export const setUploadMode = async ({ wallet, domain, mode = 1, }: { domain: NetworkDomain wallet: ActiveWallet /** Upload mode: * - 0 = low * - 1 = medium (default) * - 2 = high */ mode: 0 | 1 | 2 }): Promise<void> => { const goWasm = await getWasm({ domain, wallet }) return goWasm.sdk.setUploadMode(mode) } type BulkUploadOption = { allocationId: string webstreaming: boolean isUpdate: boolean isRepair: boolean /** Number of blocks to upload per request @default 100 */ numBlocks: number file: File remotePath: string /** Whether to encrypt the file */ encrypt: boolean /** Use `fileToByteString` to generate the thumbnail byte string, or assign an empty string if unavailable. */ thumbnailBytes: string /** Callback function will be invoked with progress updates */ callback?: ( totalBytes: number, completedBytes: number, fileName: string, blobURL: string, error: string ) => void /** @optional */ uploadId?: string /** @deprecated */ webStreaming?: boolean } const sdkMultiUpload = async ({ wallet, domain, bulkUploadOptions, }: { domain: NetworkDomain wallet: ActiveWallet bulkUploadOptions: BulkUploadOption[] }): Promise<{ success?: boolean; error?: string }> => { const goWasm = await getWasm({ domain, wallet }) try { const jsonOptions = JSON.stringify(bulkUploadOptions) const result = await goWasm.sdk.multiUpload(jsonOptions) if (result.error) throw new Error(result.error) return result } catch (e) { throw errorOut('multiUpload', e) } } /** Upload multiple files in a batch. Also, used to resume a paused upload. * * NOTE: Keep the batch size under 50 files. */ export const multiUpload = async ({ wallet, domain, bulkUploadOptions: options, }: { domain: NetworkDomain wallet: ActiveWallet bulkUploadOptions: BulkUploadOption[] }): Promise<{ success?: boolean; error?: string }> => { const g = globalCtx() const bridge = getBridge() const start = bridge.glob.index const opts = options.map(obj => { const i = bridge.glob.index bridge.glob.index++ const readChunkFuncName = '__zcn_upload_reader_' + i.toString() const callbackFuncName = '__zcn_upload_callback_' + i.toString() g[readChunkFuncName] = async (offset: number, chunkSize: number) => { const chunk = await readChunk(offset, chunkSize, obj.file) return chunk.buffer } const callback = obj.callback if (callback) { g[callbackFuncName] = async ( totalBytes: number, completedBytes: number, fileName: string, blobUrl: string, error: string ) => callback(totalBytes, completedBytes, fileName, blobUrl, error) } delete obj.uploadId return { ...obj, fileSize: obj.file.size, readChunkFuncName, callbackFuncName, } }) const end = bridge.glob.index const result = await sdkMultiUpload({ wallet, domain, bulkUploadOptions: opts, }) for (let i = start; i < end; i++) { g['__zcn_upload_reader_' + i.toString()] = null g['__zcn_upload_callback_' + i.toString()] = null } return result } type MultiOperationOption = { /** Type of operation: 'copy', 'move', 'delete', or 'createdir' */ operationType: 'copy' | 'move' | 'delete' | 'createdir' /** Remote path of the file/directory */ remotePath: string /** Destination name (only for rename operation) */ destName?: string /** Destination path (required for copy and move operations) */ destPath?: string } /** Perform multiple operations (copy, move, delete, create directory) together */ export const multiOperation = async ({ wallet, domain, allocationId, operations, }: { domain: NetworkDomain wallet: ActiveWallet allocationId: string operations: MultiOperationOption[] }): Promise<void> => { const goWasm = await getWasm({ domain, wallet }) try { const jsonOptions = JSON.stringify(operations) const errorMessage = (await goWasm.sdk.multiOperation( allocationId, jsonOptions )) as string if (errorMessage) throw new Error(errorMessage) } catch (e) { throw errorOut('multiOperation', e) } } /** List the files for a given allocation ID and remote path */ export const listObjects = async ({ wallet, domain, allocationId, remotePath, offset = 0, pageLimit = -1, }: { domain: NetworkDomain wallet: ActiveWallet allocationId: string /** The remote path of the file */ remotePath: string /** The pagination offset for the list. @default 0 (turn off pagination)*/ offset: number /** The number of items per page. @default -1 (turn off pagination) */ pageLimit: number }): Promise<ListResult> => { const goWasm = await getWasm({ domain, wallet }) const listResult = await goWasm.sdk.listObjects( allocationId, remotePath, offset, pageLimit ) return listResult } /** List objects from an auth ticket. It's useful for accessing a shared source by a non-owner */ export const listObjectsFromAuthTicket = async ({ wallet, domain, allocationId, authTicket, lookupHash, offset = 0, pageLimit = -1, }: { domain: NetworkDomain wallet: ActiveWallet allocationId: string /** The auth ticket, provided by a non-owner to access a shared source */ authTicket: string /** The lookup hash for the file */ lookupHash: string /** The pagination offset for the list. @default 0 (turn off pagination)*/ offset: number /** The number of items per page. @default -1 (turn off pagination) */ pageLimit: number }): Promise<ListResult> => { const goWasm = await getWasm({ domain, wallet }) const listResult = await goWasm.sdk.listObjectsFromAuthTicket( allocationId, authTicket, lookupHash, offset, pageLimit ) return listResult } // TODO: listSharedFiles /** Create a directory on blobbers */ export const createDir = async ({ wallet, domain, allocationId, remotePath, }: { domain: NetworkDomain wallet: ActiveWallet allocationId: string /** The remote path where the directory will be created */ remotePath: string }): Promise<void> => { const goWasm = await getWasm({ domain, wallet }) return goWasm.sdk.createDir(allocationId, remotePath) } /** Downloads a specified range of blocks from a file. */ export const downloadBlocks = async ({ wallet, domain, allocationId, remotePath, authTicket = '', lookupHash, writeChunkFunc, startBlock, endBlock, }: { domain: NetworkDomain wallet: ActiveWallet allocationId: string remotePath: string /** @optional Required only for download of a shared file (non-owner) */ authTicket?: string /** Lookup hash of the file, which is used to locate the file if remotepath and allocation id are not provided */ lookupHash?: string writeChunkFunc: ( lookupHash: string, chunk: Uint8Array, offset: number ) => void startBlock: number endBlock: number }): Promise<Uint8Array> => { const goWasm = await getWasm({ domain, wallet }) let writeChunkFuncName = '' if (writeChunkFunc) { writeChunkFuncName = `multiDownloadCallback_${Date.now()}` window[writeChunkFuncName] = writeChunkFunc } try { const data = await goWasm.sdk.downloadBlocks( allocationId, remotePath, authTicket, lookupHash, writeChunkFuncName, startBlock, endBlock ) return data } catch (e) { throw errorOut('downloadBlocks', e) } finally { // TODO: check if this is to be avoided or not if (writeChunkFuncName) delete window[writeChunkFuncName] } } type FileStats = { CreatedAt: string blobber_id: string blobber_url: string blockchain_aware: boolean file_id: string last_challenge_txn: string name: string num_of_block_downloads: number num_of_blocks: number num_of_challenges: number num_of_failed_challenges: number num_of_updates: number path: string path_hash: string size: number write_marker_txn: string } /** Fetches the file details */ export const getFileStats = async ({ wallet, domain, allocationId, remotePath, }: { domain: NetworkDomain wallet: ActiveWallet allocationId: string remotePath: string }): Promise<FileStats[]> => { const goWasm = await getWasm({ domain, wallet }) const fileStats = await goWasm.sdk.getFileStats(allocationId, remotePath) return fileStats } // TODO: improve docs details /** Updates the settings for a blobber. */ export const updateBlobberSettings = async ({ wallet, domain, blobberSettings, }: { domain: NetworkDomain wallet: ActiveWallet /** The new settings to apply to the blobber */ blobberSettings: Blobber }): Promise<Transaction> => { const goWasm = await getWasm({ domain, wallet }) try { const blobberSettingsJson = JSON.stringify(blobberSettings) const transaction = await goWasm.sdk.updateBlobberSettings( blobberSettingsJson ) return transaction } catch (e) { throw errorOut('updateBlobberSettings', e) } } type FileInfo = { actual_size: number created_at: string encrypted_key: string hash: string lookup_hash: string mimetype: string size: number type: string updated_at: string name: string path: string } /** Lists all files in an allocation from the blobbers. * @deprecated This will consume too much memory and time if there are many nested folders with many files. Prefer using `listObjects` instead. */ export const getRemoteFileMap = async ({ wallet, domain, allocationId, }: { domain: NetworkDomain wallet: ActiveWallet allocationId: string }): Promise<FileInfo[]> => { const goWasm = await getWasm({ domain, wallet }) return await goWasm.sdk.getRemoteFileMap(allocationId) } /** Get list of active blobbers */ export const getBlobbers = async ({ wallet, domain, stakable, }: { domain: NetworkDomain wallet: ActiveWallet /** flag to get only stakable blobbers */ stakable: boolean }): Promise<Blobber[]> => { const goWasm = await getWasm({ domain, wallet }) return await goWasm.sdk.getBlobbers(stakable) } /** Retrieves blobber IDs for the given list of blobber URLs. */ export const getBlobberIds = async ({ wallet, domain, blobberUrls, }: { domain: NetworkDomain wallet: ActiveWallet /** List of blobber URLs for which IDs need to be retrieved */ blobberUrls: string[] }): Promise<string[]> => { const goWasm = await getWasm({ domain, wallet }) const blobberIds = await goWasm.sdk.getBlobberIds(blobberUrls) return blobberIds } /** * GetContainers returns all the running containers in a given domain exposing the `{requestDomain}/endpoints/{endpointID}/docker/containers/json` endpoint * * The request should be authenticated with the given username and password, by first creating an auth token then issuing the request. * * @returns List of containers */ export const getContainers = async ({ wallet, domain, username, password, requestDomain, }: { wallet: ActiveWallet domain: NetworkDomain /** Username to authenticate with */ username: string /** Password to authenticate with */ password: string /** Domain to issue the request to */ requestDomain: string }): Promise<Array<Record<string, any>>> => { const goWasm = await getWasm({ domain, wallet }) const containers = await goWasm.sdk.getcontainers( username, password, requestDomain ) return containers } /** * UpdateContainer updates the given container ID with a new image ID in a given domain. * The domain should expose the docker API endpoints under `{requestDomain}/endpoints/{endpointID}/docker`. * The request should be authenticated with the given username and password, by first creating an auth token then issuing the request. * * @returns A map containing the response from the update operation. */ export const updateContainer = async ({ wallet, domain, username, password, containerID, newImageID, requestDomain, }: { wallet: ActiveWallet domain: NetworkDomain /** Username to authenticate with */ username: string /** Password to authenticate with */ password: string /** Domain to issue the request to */ requestDomain: string /** Container ID to update */ containerID: string /** New Image ID to update the container with */ newImageID: string }): Promise<Record<string, any>> => { const goWasm = await getWasm({ domain, wallet }) const response = await goWasm.sdk.updatecontainer( username, password, requestDomain, containerID, newImageID ) return response } /** * searchContainer searches for a container with a given name in a given domain exposing the `{requestDomain}/endpoints/{endpointID}/docker/containers/json` endpoint. * The request should be authenticated with the given username and password, by first creating an auth token then issuing the request. * The response is a list of containers in JSON format that match the given name. * * @returns List of containers matching the name */ export const searchContainer = async ({ wallet, domain, username, password, name, requestDomain, }: { wallet: ActiveWallet domain: NetworkDomain /** Username to authenticate with */ username: string /** Password to authenticate with */ password: string /** Domain to issue the request to */ requestDomain: string /** Name of the container to search for */ name: string }): Promise<Array<Record<string, any>>> => { const goWasm = await getWasm({ domain, wallet }) const containers = await goWasm.sdk.searchcontainer( username, password, requestDomain, name ) return containers } /** Cancel the upload operation of the file. */ export const cancelUpload = async ({ wallet, domain, allocationId, remotePath, }: { wallet: ActiveWallet domain: NetworkDomain allocationId: string /** Remote path of the file */ remotePath: string }): Promise<void> => { const goWasm = await getWasm({ domain, wallet }) return await goWasm.sdk.cancelUpload(allocationId, remotePath) } /** Pause the upload operation of the file. */ export const pauseUpload = async ({ wallet, domain, allocationId, remotePath, }: { wallet: ActiveWallet domain: NetworkDomain allocationId: string /** Remote path of the file */ remotePath: string }): Promise<void> => { const goWasm = await getWasm({ domain, wallet }) return await goWasm.sdk.pauseUpload(allocationId, remotePath) } /** Get file metadata by name. (File Search) */ export const getFileMetaByName = async <T extends FileRefByName>({ wallet, domain, allocationId, fileName, modifyFileRef, }: { wallet: ActiveWallet domain: NetworkDomain allocationId: string fileName: string modifyFileRef?: (file: FileRefByName) => T }): Promise<T[] | FileRefByName[]> => { const goWasm = await getWasm({ domain, wallet }) try { const golangObjectResponse = (await goWasm.sdk.getFileMetaByName( allocationId, fileName )) as GolangFileRefByName[] const covertGolangToJSFileRefByName = <T extends FileRefByName>( fileRefs: GolangFileRefByName[], modifyFileRef?: (file: FileRefByName) => T ): T[] | FileRefByName[] => { return fileRefs.flatMap(fileRef => { const file: FileRefByName = { name: fileRef.Name, type: fileRef.Type, path: fileRef.Path, lookup_hash: fileRef.LookupHash, hash: fileRef.Hash, mimetype: fileRef.MimeType, size: fileRef.Size, num_blocks: fileRef.NumBlocks, actual_size: fileRef.ActualFileSize, actual_num_blocks: fileRef.ActualNumBlocks, encryption_key: fileRef.EncryptedKey, file_meta_hash: fileRef.FileMetaHash, thumbnail_hash: fileRef.ThumbnailHash, actual_thumbnail_size: fileRef.ActualThumbnailSize, actual_thumbnail_hash: fileRef.ActualThumbnailHash, collaborators: fileRef.Collaborators, created_at: fileRef.CreatedAt, updated_at: fileRef.UpdatedAt, fromSearch: true, } if (modifyFileRef) { const customFile = modifyFileRef(file) return customFile ? [customFile] : [] } return [file] }) } const jsObjectResponse = covertGolangToJSFileRefByName( golangObjectResponse, modifyFileRef ) return jsObjectResponse } catch (e) { throw errorOut('getFileMetaByName', e) } } /** Download files in a directory recursively. */ export const downloadDirectory = async ({ wallet, domain, allocationId, remotePath, authTicket = '', callback, }: { domain: NetworkDomain wallet: ActiveWallet allocationId: string /** @optional Required only for download of a shared directory (non-owner) */ authTicket?: string /** Remote path of the directory to download */ remotePath: string /** Callback function will be invoked with progress updates */ callback?: ( totalBytes: number, completedBytes: number, fileName: string, blobURL: string, error?: string ) => void }): Promise<void> => { const goWasm = await getWasm({ domain, wallet }) let callbackFuncName = '' if (callback) { callbackFuncName = `downloadDirectoryCallback_${Date.now()}` window[callbackFuncName] = callback } try { await goWasm.sdk.downloadDirectory( allocationId, remotePath, authTicket, callbackFuncName ) } catch (e) { throw errorOut('downloadDirectory', e) } finally { // TODO: check if this is to be avoided or not if (callbackFuncName) delete window[callbackFuncName] } } /** Cancel the download of a directory. */ export const cancelDownloadDirectory = async ({ wallet, domain, remotePath, }: { domain: NetworkDomain wallet: ActiveWallet /** Remote path of the directory */ remotePath: string }): Promise<void> => { const goWasm = await getWasm({ domain, wallet }) return goWasm.sdk.cancelDownloadDirectory(remotePath) } /** Cancels the download of a specified range of blocks from a file. */ export const cancelDownloadBlocks = async ({ wallet, domain, allocationId, remotePath, start, end, }: { domain: NetworkDomain wallet: ActiveWallet /** The allocation ID associated with the file */ allocationId: string /** The remote path where the file is located */ remotePath: string /** The start block previously used in the downloadBlocks call */ start: number /** The endi block previously used in the downloadBlocks call */ end: number }): Promise<void> => { const goWasm = await getWasm({ domain, wallet }) return goWasm.sdk.cancelDownloadBlocks(allocationId, remotePath, start, end) } /* TODO: skipped upload. because we are passing fileBytes which can take up a lot of memory */ // type BulkUploadOption = { // remotePath: string // fileBytes: Uint8Array // thumbnailBytes?: Uint8Array // webStreaming?: boolean // encrypt?: boolean // isUpdate?: boolean // isRepair?: boolean // numBlocks?: number // } // export const upload = async ({ // wallet, // domain, // allocationId, // remotePath, // fileBytes, // thumbnailBytes, // webStreaming = false, // encrypt, // isUpdate, // isRepair, // numBlocks, // }: { // domain: NetworkDomain // wallet: ActiveWallet // allocationId: string // /** Remote path where the file should be stored */ // remotePath: string // /** File data in bytes */ // fileBytes: Uint8Array // /** Thumbnail data in string of byte numbers. For example: strings like `128,32,6,...` */ // thumbnailBytes: string // /** @deprecated */ // webStreaming: boolean // /** Enable encryption for the uploaded file */ // encrypt: boolean // /** Whether this is an update operation */ // isUpdate: boolean // /** Whether this is a repair operation */ // isRepair: boolean // /** Number of blocks to upload */ // numBlocks: number // }) => { // const goWasm = await getWasm({ domain, wallet }) // try { // const response = (await goWasm.sdk.upload( // allocationId, // remotePath, // fileBytes, // thumbnailBytes, // webStreaming, // encrypt, // isUpdate, // isRepair, // numBlocks // )) as { // /** Indicates whether the command was successful */ // commandSuccess?: boolean // /** Error message if the operation failed */ // error?: string // } // if (response.error) throw new Error(response.error) // return response // } catch (e) { // console.error('upload: ', e) // if (typeof e === 'string') throw new Error(e) // else if (e instanceof Error) throw e // else throw new Error(`upload: Unknown error`) // } // }