UNPKG

@bsv/sdk

Version:

BSV Blockchain Software Development Kit

203 lines 8.95 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.StorageUploader = void 0; const AuthFetch_js_1 = require("../auth/clients/AuthFetch.js"); const StorageUtils = __importStar(require("./StorageUtils.js")); /** * The StorageUploader class provides client-side methods for: * - Uploading files with a specified retention period * - Finding file metadata by UHRP URL * - Listing all user uploads * - Renewing an existing advertisement's expiry time */ class StorageUploader { /** * Creates a new StorageUploader instance. * @param {UploaderConfig} config - An object containing the storage server's URL and a wallet interface */ constructor(config) { this.baseURL = config.storageURL; this.authFetch = new AuthFetch_js_1.AuthFetch(config.wallet); } /** * Requests information from the server to upload a file (including presigned URL and headers). * @private * @param {number} fileSize - The size of the file, in bytes * @param {number} retentionPeriod - The desired hosting time, in minutes * @returns {Promise<{ uploadURL: string; requiredHeaders: Record<string, string>; amount?: number }>} * @throws {Error} If the server returns a non-OK response or an error status */ async getUploadInfo(fileSize, retentionPeriod) { const url = `${this.baseURL}/upload`; const body = { fileSize, retentionPeriod }; const response = await this.authFetch.fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); if (!response.ok) { throw new Error(`Upload info request failed: HTTP ${response.status}`); } const data = await response.json(); if (data.status === 'error') { throw new Error('Upload route returned an error.'); } return { uploadURL: data.uploadURL, requiredHeaders: data.requiredHeaders, amount: data.amount }; } /** * Performs the actual file upload (HTTP PUT) to the presigned URL returned by the server. * @private * @param {string} uploadURL - The presigned URL where the file is to be uploaded * @param {UploadableFile} file - The file to upload, including its raw data and MIME type * @param {Record<string, string>} requiredHeaders - Additional headers required by the server (e.g. content-length) * @returns {Promise<UploadFileResult>} An object indicating whether publishing was successful and the resulting UHRP URL * @throws {Error} If the server returns a non-OK response */ async uploadFile(uploadURL, file, requiredHeaders) { const body = file.data instanceof Uint8Array ? file.data : Uint8Array.from(file.data); const response = await fetch(uploadURL, { method: 'PUT', body, headers: { 'Content-Type': file.type, ...requiredHeaders } }); if (!response.ok) { throw new Error(`File upload failed: HTTP ${response.status}`); } const uhrpURL = StorageUtils.getURLForFile(body); return { published: true, uhrpURL }; } /** * Publishes a file to the storage server with the specified retention period. * * This will: * 1. Request an upload URL from the server. * 2. Perform an HTTP PUT to upload the file’s raw bytes. * 3. Return a UHRP URL referencing the file once published. * * @param {Object} params * @param {UploadableFile} params.file - The file data + type * @param {number} params.retentionPeriod - Number of minutes to host the file * @returns {Promise<UploadFileResult>} An object with the file's UHRP URL * @throws {Error} If the server or upload step returns a non-OK response */ async publishFile(params) { const { file, retentionPeriod } = params; const data = file.data instanceof Uint8Array ? file.data : Uint8Array.from(file.data); const fileSize = data.byteLength; const { uploadURL, requiredHeaders } = await this.getUploadInfo(fileSize, retentionPeriod); return await this.uploadFile(uploadURL, { data, type: file.type }, requiredHeaders); } /** * Retrieves metadata for a file matching the given UHRP URL from the `/find` route. * @param {string} uhrpUrl - The UHRP URL, e.g. "uhrp://abcd..." * @returns {Promise<FindFileData>} An object with file name, size, MIME type, and expiry time * @throws {Error} If the server or the route returns an error */ async findFile(uhrpUrl) { const url = new URL(`${this.baseURL}/find`); url.searchParams.set('uhrpUrl', uhrpUrl); const response = await this.authFetch.fetch(url.toString(), { method: 'GET' }); if (!response.ok) { throw new Error(`findFile request failed: HTTP ${response.status}`); } const data = await response.json(); if (data.status === 'error') { const errCode = data.code ?? 'unknown-code'; const errDesc = data.description ?? 'no-description'; throw new Error(`findFile returned an error: ${errCode} - ${errDesc}`); } return data.data; } /** * Lists all advertisements belonging to the user from the `/list` route. * @returns {Promise<any>} The array of uploads returned by the server * @throws {Error} If the server or the route returns an error */ async listUploads() { const url = `${this.baseURL}/list`; const response = await this.authFetch.fetch(url, { method: 'GET' }); if (!response.ok) { throw new Error(`listUploads request failed: HTTP ${response.status}`); } const data = await response.json(); if (data.status === 'error') { const errCode = data.code ?? 'unknown-code'; const errDesc = data.description ?? 'no-description'; throw new Error(`listUploads returned an error: ${errCode} - ${errDesc}`); } return data.uploads; } /** * Renews the hosting time for an existing file advertisement identified by uhrpUrl. * Calls the `/renew` route to add `additionalMinutes` to the GCS customTime * and re-mint the advertisement token on-chain. * * @param {string} uhrpUrl - The UHRP URL of the file (e.g., "uhrp://abcd1234...") * @param {number} additionalMinutes - The number of minutes to extend * @returns {Promise<RenewFileResult>} An object with the new and previous expiry times, plus any cost * @throws {Error} If the request fails or the server returns an error */ async renewFile(uhrpUrl, additionalMinutes) { const url = `${this.baseURL}/renew`; const body = { uhrpUrl, additionalMinutes }; const response = await this.authFetch.fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); if (!response.ok) { throw new Error(`renewFile request failed: HTTP ${response.status}`); } const data = await response.json(); if (data.status === 'error') { const errCode = data.code ?? 'unknown-code'; const errDesc = data.description ?? 'no-description'; throw new Error(`renewFile returned an error: ${errCode} - ${errDesc}`); } return { status: data.status, prevExpiryTime: data.prevExpiryTime, newExpiryTime: data.newExpiryTime, amount: data.amount }; } } exports.StorageUploader = StorageUploader; //# sourceMappingURL=StorageUploader.js.map