"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _utils = require("./utils/index.cjs"); var _aws4fetch = require("aws4fetch"); const DRIVER_NAME = "s3"; module.exports = (0, _utils.defineDriver)(options => { let _awsClient; const getAwsClient = () => { if (!_awsClient) { if (!options.accessKeyId) { throw (0, _utils.createRequiredError)(DRIVER_NAME, "accessKeyId"); } if (!options.secretAccessKey) { throw (0, _utils.createRequiredError)(DRIVER_NAME, "secretAccessKey"); } if (!options.endpoint) { throw (0, _utils.createRequiredError)(DRIVER_NAME, "endpoint"); } if (!options.region) { throw (0, _utils.createRequiredError)(DRIVER_NAME, "region"); } _awsClient = new _aws4fetch.AwsClient({ service: "s3", accessKeyId: options.accessKeyId, secretAccessKey: options.secretAccessKey, region: options.region }); } return _awsClient; }; const baseURL = `${options.endpoint.replace(/\/$/, "")}/${options.bucket || ""}`; const url = (key = "") => `${baseURL}/${(0, _utils.normalizeKey)(key, "/")}`; const awsFetch = async (url2, opts) => { const request = await getAwsClient().sign(url2, opts); const res = await fetch(request); if (!res.ok) { if (res.status === 404) { return null; } throw (0, _utils.createError)(DRIVER_NAME, `[${request.method}] ${url2}: ${res.status} ${res.statusText} ${await res.text()}`); } return res; }; const headObject = async key => { const res = await awsFetch(url(key), { method: "HEAD" }); if (!res) { return null; } const metaHeaders = {}; for (const [key2, value] of res.headers.entries()) { const match = /x-amz-meta-(.*)/.exec(key2); if (match?.[1]) { metaHeaders[match[1]] = value; } } return metaHeaders; }; const listObjects = async prefix => { const res = await awsFetch(baseURL).then(r => r?.text()); if (!res) { console.log("no list", prefix ? `${baseURL}?prefix=${prefix}` : baseURL); return null; } return parseList(res); }; const getObject = key => { return awsFetch(url(key)); }; const putObject = async (key, value) => { return awsFetch(url(key), { method: "PUT", body: value }); }; const deleteObject = async key => { return awsFetch(url(key), { method: "DELETE" }).then(r => { if (r?.status !== 204 && r?.status !== 200) { throw (0, _utils.createError)(DRIVER_NAME, `Failed to delete ${key}`); } }); }; const deleteObjects = async base => { const keys = await listObjects(base); if (!keys?.length) { return null; } if (options.bulkDelete === false) { await Promise.all(keys.map(key => deleteObject(key))); } else { const body = deleteKeysReq(keys); await awsFetch(`${baseURL}?delete`, { method: "POST", headers: { "x-amz-checksum-sha256": await sha256Base64(body) }, body }); } }; return { name: DRIVER_NAME, options, getItem(key) { return getObject(key).then(res => res ? res.text() : null); }, getItemRaw(key) { return getObject(key).then(res => res ? res.arrayBuffer() : null); }, async setItem(key, value) { await putObject(key, value); }, async setItemRaw(key, value) { await putObject(key, value); }, getMeta(key) { return headObject(key); }, hasItem(key) { return headObject(key).then(meta => !!meta); }, getKeys(base) { return listObjects(base).then(keys => keys || []); }, async removeItem(key) { await deleteObject(key); }, async clear(base) { await deleteObjects(base); } }; }); function deleteKeysReq(keys) { return `${keys.map(key => { key = key.replace(/&/g, "&").replace(//g, ">"); return /* xml */`${key}`; }).join("")}`; } async function sha256Base64(str) { const buffer = new TextEncoder().encode(str); const hash = await crypto.subtle.digest("SHA-256", buffer); const bytes = new Uint8Array(hash); const binaryString = String.fromCharCode(...bytes); return btoa(binaryString); } function parseList(xml) { if (!xml.startsWith("]*>([\s\S]*)<\/ListBucketResult>/)?.[1]; if (!listBucketResult) { throw new Error("Missing "); } const contents = listBucketResult.match(/]*>([\s\S]*?)<\/Contents>/g); if (!contents?.length) { return []; } return contents.map(content => { const key = content.match(/([\s\S]+?)<\/Key>/)?.[1]; return key; }).filter(Boolean); }