/** Minimal ustar writing shared by repository archives and action archives. */
export const TAR_BLOCK = 512;
const TAR_NAME_BYTES = 100;
const TAR_PREFIX_BYTES = 155;
const encoder = new TextEncoder();
function octal(value: number, length: number): string {
return value.toString(8).padStart(length - 1, "0") + "\0";
}
function writeString(target: Uint8Array, offset: number, length: number, value: string): void {
target.set(encoder.encode(value).subarray(0, length), offset);
}
/** A ustar header, or null when the path can't be represented. `mtimeSeconds` defaults to now. */
export function tarHeader(
path: string,
size: number,
mode: number,
type: "0" | "2" | "5",
linkTarget = "",
mtimeSeconds = Math.floor(Date.now() / 1000),
): Uint8Array<ArrayBuffer> | null {
let name = path;
let prefix = "";
if (encoder.encode(name).byteLength > TAR_NAME_BYTES) {
const split = path.lastIndexOf("/", TAR_PREFIX_BYTES);
if (split <= 0) return null;
prefix = path.slice(0, split);
name = path.slice(split + 1);
if (encoder.encode(name).byteLength > TAR_NAME_BYTES || encoder.encode(prefix).byteLength > TAR_PREFIX_BYTES)
return null;
}
const header = new Uint8Array(TAR_BLOCK);
writeString(header, 0, 100, name);
writeString(header, 100, 8, octal(mode, 8));
writeString(header, 108, 8, octal(0, 8));
writeString(header, 116, 8, octal(0, 8));
writeString(header, 124, 12, octal(size, 12));
writeString(header, 136, 12, octal(mtimeSeconds, 12));