@push.rocks/smartbucket
Version:
A TypeScript library providing a cloud-agnostic interface for managing object storage with functionalities like bucket management, file and directory operations, and advanced features such as metadata handling and file locking.
215 lines • 16.8 kB
JavaScript
import * as plugins from './plugins.js';
import * as interfaces from './interfaces.js';
export const reducePathDescriptorToPath = async (pathDescriptorArg) => {
let returnPath = ``;
if (pathDescriptorArg.directory) {
if (pathDescriptorArg.path && plugins.path.isAbsolute(pathDescriptorArg.path)) {
console.warn('Directory is being ignored when path is absolute.');
returnPath = pathDescriptorArg.path;
}
else if (pathDescriptorArg.path) {
returnPath = plugins.path.join(pathDescriptorArg.directory.getBasePath(), pathDescriptorArg.path);
}
}
else if (pathDescriptorArg.path) {
returnPath = pathDescriptorArg.path;
}
else {
throw new Error('You must specify either a path or a directory.');
}
if (returnPath.startsWith('/')) {
returnPath = returnPath.substring(1);
}
return returnPath;
};
function coerceBooleanMaybe(value) {
if (typeof value === 'boolean')
return { value };
if (typeof value === 'string') {
const v = value.trim().toLowerCase();
if (v === 'true' || v === '1') {
return {
value: true,
warning: {
code: 'SBK_S3_COERCED_USESSL',
message: `Coerced useSsl='${value}' (string) to boolean true.`
}
};
}
if (v === 'false' || v === '0') {
return {
value: false,
warning: {
code: 'SBK_S3_COERCED_USESSL',
message: `Coerced useSsl='${value}' (string) to boolean false.`
}
};
}
}
return { value: undefined };
}
function coercePortMaybe(port) {
if (port === undefined || port === null || port === '')
return { value: undefined };
const n = typeof port === 'number' ? port : Number(String(port).trim());
if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0 || n > 65535) {
return {
value: undefined,
warning: {
code: 'SBK_S3_INVALID_PORT',
message: `Invalid port '${String(port)}' - expected integer in [1..65535].`
}
};
}
return { value: n };
}
function sanitizeEndpointString(raw) {
const warnings = [];
let s = String(raw ?? '').trim();
if (s !== String(raw ?? '')) {
warnings.push({
code: 'SBK_S3_TRIMMED_ENDPOINT',
message: 'Trimmed surrounding whitespace from endpoint.'
});
}
return { value: s, warnings };
}
function parseEndpointHostPort(endpoint, provisionalProtocol) {
let url;
const extras = {};
// Check if endpoint already has a scheme
const hasScheme = /^https?:\/\//i.test(endpoint);
// Try parsing as full URL first
try {
if (hasScheme) {
url = new URL(endpoint);
}
else {
// Not a full URL; try host[:port] by attaching provisional scheme
// Remove anything after first '/' for safety
const cleanEndpoint = endpoint.replace(/\/.*/, '');
url = new URL(`${provisionalProtocol}://${cleanEndpoint}`);
}
}
catch (e) {
throw new Error(`Unable to parse endpoint '${endpoint}'.`);
}
// Check for dropped components
if (url.username || url.password)
extras.droppedCreds = true;
if (url.pathname && url.pathname !== '/')
extras.droppedPath = true;
if (url.search)
extras.droppedQuery = true;
const hadScheme = hasScheme;
const host = url.hostname; // hostnames lowercased by URL; IPs preserved
const port = url.port ? Number(url.port) : undefined;
return { hadScheme, host, port, extras };
}
export function normalizeStorageDescriptor(input, logger) {
const warnings = [];
const logWarn = (w) => {
warnings.push(w);
if (logger) {
logger.warn(`[SmartBucket] ${w.code}: ${w.message}`);
}
else {
console.warn(`[SmartBucket] ${w.code}: ${w.message}`);
}
};
// Coerce and sanitize inputs
const { value: coercedUseSsl, warning: useSslWarn } = coerceBooleanMaybe(input.useSsl);
if (useSslWarn)
logWarn(useSslWarn);
const { value: coercedPort, warning: portWarn } = coercePortMaybe(input.port);
if (portWarn)
logWarn(portWarn);
const { value: endpointStr, warnings: endpointSanWarnings } = sanitizeEndpointString(input.endpoint);
endpointSanWarnings.forEach(logWarn);
if (!endpointStr) {
throw new Error('Storage endpoint is required (got empty string). Provide hostname or URL.');
}
// Provisional protocol selection for parsing host:port forms
const provisionalProtocol = coercedUseSsl === false ? 'http' : 'https';
const { hadScheme, host, port: epPort, extras } = parseEndpointHostPort(endpointStr, provisionalProtocol);
if (extras.droppedCreds) {
logWarn({
code: 'SBK_S3_DROPPED_CREDENTIALS',
message: 'Ignored credentials in endpoint URL.'
});
}
if (extras.droppedPath) {
logWarn({
code: 'SBK_S3_DROPPED_PATH',
message: 'Removed path segment from endpoint URL; S3 endpoint should be host[:port] only.'
});
}
if (extras.droppedQuery) {
logWarn({
code: 'SBK_S3_DROPPED_QUERY',
message: 'Removed query string from endpoint URL; S3 endpoint should be host[:port] only.'
});
}
// Final protocol decision
let finalProtocol;
if (hadScheme) {
// Scheme from endpoint wins
const schemeFromEndpoint = endpointStr.trim().toLowerCase().startsWith('http://') ? 'http' : 'https';
finalProtocol = schemeFromEndpoint;
if (typeof coercedUseSsl === 'boolean') {
const expected = coercedUseSsl ? 'https' : 'http';
if (expected !== finalProtocol) {
logWarn({
code: 'SBK_S3_SCHEME_CONFLICT',
message: `useSsl=${String(coercedUseSsl)} conflicts with endpoint scheme '${finalProtocol}'; using endpoint scheme.`
});
}
}
}
else {
if (typeof coercedUseSsl === 'boolean') {
finalProtocol = coercedUseSsl ? 'https' : 'http';
}
else {
finalProtocol = 'https';
logWarn({
code: 'SBK_S3_GUESSED_PROTOCOL',
message: "No scheme in endpoint and useSsl not provided; defaulting to 'https'."
});
}
}
// Final port decision
let finalPort = undefined;
if (coercedPort !== undefined && epPort !== undefined && coercedPort !== epPort) {
logWarn({
code: 'SBK_S3_PORT_CONFLICT',
message: `Port in config (${coercedPort}) conflicts with endpoint port (${epPort}); using config port.`
});
finalPort = coercedPort;
}
else {
finalPort = (coercedPort !== undefined) ? coercedPort : epPort;
}
// Build canonical endpoint URL (origin only, no trailing slash)
const url = new URL(`${finalProtocol}://${host}`);
if (finalPort !== undefined)
url.port = String(finalPort);
const endpointUrl = url.origin;
const region = input.region || 'us-east-1';
return {
normalized: {
endpointUrl,
host,
protocol: finalProtocol,
port: finalPort,
region,
credentials: {
accessKeyId: input.accessKey,
secretAccessKey: input.accessSecret,
},
forcePathStyle: true,
},
warnings,
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2hlbHBlcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxLQUFLLFVBQVUsTUFBTSxpQkFBaUIsQ0FBQztBQUU5QyxNQUFNLENBQUMsTUFBTSwwQkFBMEIsR0FBRyxLQUFLLEVBQUUsaUJBQTRDLEVBQW1CLEVBQUU7SUFDaEgsSUFBSSxVQUFVLEdBQUcsRUFBRSxDQUFBO0lBQ25CLElBQUksaUJBQWlCLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDaEMsSUFBSSxpQkFBaUIsQ0FBQyxJQUFJLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUM5RSxPQUFPLENBQUMsSUFBSSxDQUFDLG1EQUFtRCxDQUFDLENBQUM7WUFDbEUsVUFBVSxHQUFHLGlCQUFpQixDQUFDLElBQUksQ0FBQztRQUN0QyxDQUFDO2FBQU0sSUFBSSxpQkFBaUIsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNsQyxVQUFVLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsU0FBUyxDQUFDLFdBQVcsRUFBRSxFQUFFLGlCQUFpQixDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3BHLENBQUM7SUFDSCxDQUFDO1NBQU0sSUFBSSxpQkFBaUIsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNsQyxVQUFVLEdBQUcsaUJBQWlCLENBQUMsSUFBSSxDQUFDO0lBQ3RDLENBQUM7U0FBTSxDQUFDO1FBQ04sTUFBTSxJQUFJLEtBQUssQ0FBQyxnREFBZ0QsQ0FBQyxDQUFDO0lBQ3BFLENBQUM7SUFDRCxJQUFJLFVBQVUsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUMvQixVQUFVLEdBQUcsVUFBVSxDQUFDLFNBQVMsQ0FBQyxDQUFD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