@sap/ams-dev
Version:
NodesJS AMS development environment
143 lines (114 loc) • 4.13 kB
JavaScript
const https = require("node:https");
const fs = require('node:fs');
const path = require('node:path');
const zlib = require("node:zlib");
const AMS_INSTANCE_HEADER = "X-Ams-Instance-Id";
const version = require("../../package.json").version;
const USER_AGENT = `@sap/ams-dev:${version}`;
function addDefaultHeaders(headers, credentials) {
headers[AMS_INSTANCE_HEADER] = credentials.authorization_instance_id;
headers["User-Agent"] = USER_AGENT;
}
function createHTTPSAgent(options) {
return new https.Agent({
cert: options.certificate,
key: options.key,
});
}
module.exports = {
addDefaultHeaders,
createHTTPSAgent,
createTgzFromDirectory
};
/**
* Creates a .tgz archive from a directory (only .dcl files)
* @param {string} dirPath - Directory to compress
* @returns {Promise<Buffer>} - The compressed .tgz buffer
*/
async function createTgzFromDirectory(dirPath) {
const files = await collectDclFiles(dirPath, dirPath);
const tarBuffer = createTarArchive(files);
return new Promise((resolve, reject) => {
zlib.gzip(tarBuffer, (err, compressed) => {
if (err) reject(err);
else resolve(compressed);
});
});
}
/**
* Recursively collect all .dcl files from a directory
*/
async function collectDclFiles(baseDir, currentDir) {
const files = [];
const entries = await fs.promises.readdir(currentDir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(currentDir, entry.name);
if (entry.isDirectory()) {
const subFiles = await collectDclFiles(baseDir, fullPath);
files.push(...subFiles);
} else if (entry.isFile() && entry.name.endsWith('.dcl')) {
const relativePath = path.relative(baseDir, fullPath);
const content = await fs.promises.readFile(fullPath);
files.push({
name: relativePath.split(path.sep).join('/'), // Normalize to forward slashes
content,
mode: 0o644,
mtime: Math.floor(Date.now() / 1000)
});
}
}
return files;
}
/**
* Create a tar archive from a list of files
*/
function createTarArchive(files) {
const blocks = [];
for (const file of files) {
const header = createTarHeader(file.name, file.content.length, file.mode, file.mtime);
blocks.push(header);
// Add file content (padded to 512-byte blocks)
const paddedSize = Math.ceil(file.content.length / 512) * 512;
const contentBlock = Buffer.alloc(paddedSize);
file.content.copy(contentBlock);
blocks.push(contentBlock);
}
// Add two empty blocks to end the tar archive
blocks.push(Buffer.alloc(512));
blocks.push(Buffer.alloc(512));
return Buffer.concat(blocks);
}
/**
* Create a tar header for a file
*/
function createTarHeader(filename, size, mode, mtime) {
const header = Buffer.alloc(512);
// Filename (0-99)
header.write(filename.slice(0, 100), 0);
// File mode (100-107) - octal
header.write(mode.toString(8).padStart(7, '0') + '\0', 100);
// Owner UID (108-115)
header.write('0000000\0', 108);
// Owner GID (116-123)
header.write('0000000\0', 116);
// File size in octal (124-135)
header.write(size.toString(8).padStart(11, '0') + '\0', 124);
// Modification time (136-147)
header.write(mtime.toString(8).padStart(11, '0') + '\0', 136);
// Checksum placeholder (148-155) - spaces for calculation
header.write(' ', 148);
// Type flag (156) - '0' for regular file
header[156] = 48; // ASCII '0'
// Link name (157-256) - empty for regular files
// USTAR magic (257-262)
header.write('ustar\0', 257);
// USTAR version (263-264)
header.write('00', 263);
// Calculate checksum (sum of all bytes in header, treating checksum field as spaces)
let checksum = 0;
for (let i = 0; i < 512; i++) {
checksum += header[i];
}
header.write(checksum.toString(8).padStart(6, '0') + '\0 ', 148);
return header;
}