UNPKG

@wttp/site

Version:

Web3 Transfer Protocol (WTTP) - Site Contracts and deployment tools

189 lines 7.99 kB
"use strict"; // Import ethers from the hardhat runtime environment when running // but allow direct import from ethers package when imported Object.defineProperty(exports, "__esModule", { value: true }); exports.fetchResource = fetchResource; exports.readDataPointsContent = readDataPointsContent; exports.isText = isText; exports.main = main; const hardhat_1 = require("hardhat"); const core_1 = require("@wttp/core"); /** * Fetches a resource directly from a WTTP site contract * * @param siteAddress - The address of the WTTP site contract * @param path - The path to the resource * @param options - Optional parameters for the request * @returns The response from the site with full content */ async function fetchResource(siteAddress, path, options = {}) { // Parameter validation if (!siteAddress) { // TODO: should validate that siteAddress is a valid address throw new Error("Site address is required"); } try { path = (0, core_1.normalizePath)(path || "/"); // should force absolute path, doesn't } catch (error) { throw new Error(`Invalid path format: ${error instanceof Error ? error.message : 'Unknown error'}`); } const { ifModifiedSince = 0, ifNoneMatch = hardhat_1.ethers.ZeroHash, range = { start: 0, end: 0 }, headRequest = false, datapoints = true, chainId } = options; console.log(`🌐 Connecting to site: ${siteAddress}`); console.log(`📄 Requesting resource: ${path}${headRequest ? ' (HEAD only)' : ''}`); // Get the site contract with error handling let siteContract; try { siteContract = await hardhat_1.ethers.getContractAt("Web3Site", siteAddress); } catch (error) { throw new Error(`Failed to connect to site contract at ${siteAddress}: ${error}`); } // Create the request - updated to new structure without requestLine wrapper const headRequest_obj = { path: path, ifModifiedSince, ifNoneMatch }; let head = { status: 404n, headerInfo: { cache: { immutableFlag: false, preset: 0n, custom: "" }, cors: { methods: 1n, origins: core_1.ORIGINS_ADMIN_ONLY, preset: 0n, custom: "" }, redirect: { code: 0n, location: "" } }, metadata: { properties: { mimeType: "0x0000", charset: "0x0000", encoding: "0x0000", language: "0x0000" }, size: 0n, version: 0n, lastModified: 0n, header: hardhat_1.ethers.ZeroHash }, etag: hardhat_1.ethers.ZeroHash }; // If it's a HEAD request, just call HEAD if (headRequest) { console.log(`Sending HEAD request for ${path} from site ${siteAddress}`); try { head = await siteContract.HEAD(headRequest_obj); } catch (error) { console.log(error.message); } return { response: { head, resource: { dataPoints: [], totalChunks: 0 } }, content: undefined }; } else { // For GET requests, use the site's GET method which returns LOCATEResponse console.log(`Fetching resource at ${path} from site ${siteAddress}`); let locateResponse; try { locateResponse = await siteContract.GET({ head: headRequest_obj, rangeChunks: range }); } catch (error) { console.log(error); locateResponse = { head: head, resource: { dataPoints: [], totalChunks: 0 } }; } // Updated to use new response structure without responseLine wrapper console.log(`Response status: ${locateResponse.head.status}`); console.log(`Found ${locateResponse.resource.dataPoints.length} data points`); // If the response is successful and user wants data (no --datapoints flag), load the content let content = undefined; if (!datapoints) { if ((locateResponse.head.status === 200n || locateResponse.head.status === 206n) && locateResponse.resource.dataPoints.length > 0) { const dataPointAddresses = locateResponse.resource.dataPoints.map(dp => dp.toString()); content = await readDataPointsContent(siteAddress, dataPointAddresses, chainId); } } return { response: locateResponse, content }; } } /** * Reads content from an array of datapoints using the DPS contract * * @param siteAddress - The address of the WTTP site (to get DPS reference) * @param dataPoints - Array of datapoint addresses * @param chainId - Optional chain ID for ESP deployments * @returns Combined content from all datapoints */ async function readDataPointsContent(siteAddress, dataPoints, chainId) { console.log(`📥 Reading content from ${dataPoints.length} datapoints...`); // Parameter validation if (!siteAddress || !dataPoints || dataPoints.length === 0) { throw new Error("Valid site address and datapoints array required"); } // Get the site contract to access DPS const siteContract = await hardhat_1.ethers.getContractAt("Web3Site", siteAddress); const dpsAddress = await siteContract.DPS(); console.log(`🔗 Loading DPS at address ${dpsAddress}...`); // Get the DPS contract const dpsContract = await hardhat_1.ethers.getContractAt("@tw3/esp/contracts/interfaces/IDataPointStorage.sol:IDataPointStorage", dpsAddress); // Read all datapoints and combine their content with progress reporting const contents = []; let totalBytesRead = 0; for (let i = 0; i < dataPoints.length; i++) { const dataPointAddress = dataPoints[i]; const progress = Math.round(((i + 1) / dataPoints.length) * 100); console.log(`📊 Reading chunk ${i + 1}/${dataPoints.length} (${progress}%): ${dataPointAddress.substring(0, 10)}...`); try { // Read the datapoint content const dataPointContent = await dpsContract.readDataPoint(dataPointAddress); const chunk = new Uint8Array(hardhat_1.ethers.toBeArray(dataPointContent)); contents.push(chunk); totalBytesRead += chunk.length; console.log(`✅ Chunk ${i + 1} read: ${chunk.length} bytes`); } catch (error) { console.error(`❌ Failed to read datapoint ${dataPointAddress}:`, error); throw new Error(`Failed to read datapoint ${i + 1}/${dataPoints.length}: ${error}`); } } // Combine all content chunks with optimized allocation console.log(`🔗 Combining ${dataPoints.length} chunks (${totalBytesRead} total bytes)...`); const combined = new Uint8Array(totalBytesRead); let offset = 0; for (let i = 0; i < contents.length; i++) { const chunk = contents[i]; combined.set(chunk, offset); offset += chunk.length; } console.log(`✅ Successfully reconstructed ${combined.length} bytes from ${dataPoints.length} chunks`); return combined; } function isText(mimeType) { return mimeType === "0x7470" || // text/plain (tp) mimeType === "0x7468" || // text/html (th) mimeType === "0x7463" || // text/css (tc) mimeType === "0x746d" || // text/markdown (tm) mimeType === "0x616a" || // application/javascript (aj) mimeType === "0x616f" || // application/json (ao) mimeType === "0x6178" || // application/xml (ax) mimeType === "0x6973"; // image/svg+xml (is) } /** * Main function to fetch a resource from a WTTP site */ async function main(siteAddress, path, options = {}) { // Fetch the resource directly from the site return await fetchResource(siteAddress, path, options); } //# sourceMappingURL=fetchResource.js.map