feat: init

This commit is contained in:
2026-02-13 22:02:30 +01:00
commit 8f9ff830fb
16711 changed files with 3307340 additions and 0 deletions

229
node_modules/nanotar/dist/index.cjs generated vendored Normal file
View File

@@ -0,0 +1,229 @@
'use strict';
const TAR_TYPE_FILE = 0;
const TAR_TYPE_DIR = 5;
function parseTar(data, opts) {
const buffer = data.buffer || data;
const files = [];
let offset = 0;
while (offset < buffer.byteLength - 512) {
let name = _readString(buffer, offset, 100);
if (name.length === 0) {
break;
}
const mode = _readString(buffer, offset + 100, 8).trim();
const uid = Number.parseInt(_readString(buffer, offset + 108, 8));
const gid = Number.parseInt(_readString(buffer, offset + 116, 8));
const size = _readNumber(buffer, offset + 124, 12);
const seek = 512 + 512 * Math.trunc(size / 512) + (size % 512 ? 512 : 0);
const mtime = _readNumber(buffer, offset + 136, 12);
const _type = _readNumber(buffer, offset + 156, 1);
const type = _type === TAR_TYPE_FILE ? "file" : _type === TAR_TYPE_DIR ? "directory" : _type;
const user = _readString(buffer, offset + 265, 32);
const group = _readString(buffer, offset + 297, 32);
name = _sanitizePath(name);
const meta = {
name,
type,
size,
attrs: {
mode,
uid,
gid,
mtime,
user,
group
}
};
if (opts?.filter && !opts.filter(meta)) {
offset += seek;
continue;
}
if (opts?.metaOnly) {
files.push(meta);
offset += seek;
continue;
}
const data2 = _type === TAR_TYPE_DIR ? undefined : new Uint8Array(buffer, offset + 512, size);
files.push({
...meta,
data: data2,
get text() {
return new TextDecoder().decode(this.data);
}
});
offset += seek;
}
return files;
}
async function parseTarGzip(data, opts = {}) {
const stream = new ReadableStream({
start(controller) {
controller.enqueue(new Uint8Array(data));
controller.close();
}
}).pipeThrough(new DecompressionStream(opts.compression ?? "gzip"));
const decompressedData = await new Response(stream).arrayBuffer();
return parseTar(decompressedData, opts);
}
function _sanitizePath(path) {
let normalized = path.replace(/\\/g, "/");
normalized = normalized.replace(/^[a-zA-Z]:\//, "");
normalized = normalized.replace(/^\/+/, "");
const hasLeadingDotSlash = normalized.startsWith("./");
const parts = normalized.split("/");
const resolved = [];
for (const part of parts) {
if (part === "..") {
resolved.pop();
} else if (part !== "." && part !== "") {
resolved.push(part);
}
}
let result = resolved.join("/");
if (hasLeadingDotSlash && !result.startsWith("./")) {
result = "./" + result;
}
if (path.endsWith("/") && !result.endsWith("/")) {
result += "/";
}
return result;
}
function _readString(buffer, offset, size) {
const view = new Uint8Array(buffer, offset, size);
const i = view.indexOf(0);
const td = new TextDecoder();
return td.decode(i === -1 ? view : view.slice(0, i));
}
function _readNumber(buffer, offset, size) {
const view = new Uint8Array(buffer, offset, size);
let str = "";
for (let i = 0; i < size; i++) {
str += String.fromCodePoint(view[i]);
}
return Number.parseInt(str, 8);
}
function createTar(files, opts = {}) {
const _files = files.map((file) => {
const data = _normalizeData(file.data);
return {
...file,
data,
size: data?.length || 0
};
});
let tarDataSize = 0;
for (let i = 0; i < files.length; i++) {
const size = _files[i].data?.length ?? 0;
tarDataSize += 512 + 512 * Math.trunc(size / 512);
if (size % 512) {
tarDataSize += 512;
}
}
let bufSize = 10240 * Math.trunc(tarDataSize / 10240);
if (tarDataSize % 10240) {
bufSize += 10240;
}
const buffer = new ArrayBuffer(bufSize);
let offset = 0;
for (const file of _files) {
const isDir = !file.data;
_writeString(buffer, file.name, offset, 100);
const mode = file.attrs?.mode ?? opts.attrs?.mode ?? (isDir ? "775" : "664");
_writeString(buffer, _leftPad(mode, 7), offset + 100, 8);
const uid = file.attrs?.uid ?? opts.attrs?.uid ?? 1e3;
_writeString(buffer, _leftPad(uid.toString(8), 7), offset + 108, 8);
const gid = file.attrs?.gid ?? opts.attrs?.gid ?? 1e3;
_writeString(buffer, _leftPad(gid.toString(8), 7), offset + 116, 8);
_writeString(buffer, _leftPad(file.size.toString(8), 11), offset + 124, 12);
const mtime = file.attrs?.mtime ?? opts.attrs?.mtime ?? Date.now();
_writeString(
buffer,
_leftPad(Math.trunc(mtime / 1e3).toString(8), 11),
offset + 136,
12
);
const type = isDir ? "5" : "0";
_writeString(buffer, type, offset + 156, 1);
_writeString(
buffer,
"ustar",
offset + 257,
6
/* magic string */
);
_writeString(
buffer,
"00",
offset + 263,
2
/* magic version */
);
const user = file.attrs?.user ?? opts.attrs?.user ?? "";
_writeString(buffer, user, offset + 265, 32);
const group = file.attrs?.group ?? opts.attrs?.group ?? "";
_writeString(buffer, group, offset + 297, 32);
_writeString(buffer, " ", offset + 148, 8);
const header = new Uint8Array(buffer, offset, 512);
let chksum = 0;
for (let i = 0; i < 512; i++) {
chksum += header[i];
}
_writeString(buffer, chksum.toString(8), offset + 148, 8);
if (!isDir) {
const destArray = new Uint8Array(buffer, offset + 512, file.size);
for (let byteIdx = 0; byteIdx < file.size; byteIdx++) {
destArray[byteIdx] = file.data[byteIdx];
}
offset += 512 * Math.trunc(file.size / 512);
if (file.size % 512) {
offset += 512;
}
}
offset += 512;
}
return new Uint8Array(buffer);
}
function createTarGzipStream(files, opts = {}) {
const buffer = createTar(files, opts);
return new ReadableStream({
start(controller) {
controller.enqueue(buffer);
controller.close();
}
}).pipeThrough(new CompressionStream(opts.compression ?? "gzip"));
}
async function createTarGzip(files, opts = {}) {
const data = await new Response(createTarGzipStream(files, opts)).arrayBuffer().then((buffer) => new Uint8Array(buffer));
return data;
}
function _writeString(buffer, str, offset, size) {
const strView = new Uint8Array(buffer, offset, size);
const te = new TextEncoder();
const written = te.encodeInto(str, strView).written;
for (let i = written; i < size; i++) {
strView[i] = 0;
}
}
function _leftPad(input, targetLength) {
return String(input).padStart(targetLength, "0");
}
function _normalizeData(data) {
if (data === null || data === undefined) {
return undefined;
}
if (typeof data === "string") {
return new TextEncoder().encode(data);
}
if (data instanceof ArrayBuffer) {
return new Uint8Array(data);
}
return data;
}
exports.createTar = createTar;
exports.createTarGzip = createTarGzip;
exports.createTarGzipStream = createTarGzipStream;
exports.parseTar = parseTar;
exports.parseTarGzip = parseTarGzip;

133
node_modules/nanotar/dist/index.d.cts generated vendored Normal file
View File

@@ -0,0 +1,133 @@
type TarFileItem<DataT = Uint8Array> = {
/**
* File name
*/
name: string;
/**
* The data associated with the file. This field is usually omitted for directories.
* @optional
*/
data?: DataT;
/**
* The attributes of the file. See {@link TarFileAttrs}.
* @optional
*/
attrs?: TarFileAttrs;
};
interface ParsedTarFileItem extends TarFileItem {
/**
* The type of file system element. It can be `"file"`, `"directory"` or an operating system specific numeric code.
*/
type: "file" | "directory" | number;
/**
* The size of the file in bytes.
*/
size: number;
/**
* The textual representation of the file data. This property is read-only.
*/
readonly text: string;
}
type ParsedTarFileItemMeta = Omit<ParsedTarFileItem, "data" | "text">;
interface TarFileAttrs {
/**
* File mode in octal (e.g., `664`) represents read, write, and execute permissions for the owner, group, and others.
*/
mode?: string;
/**
* The user ID associated with the file.
* @default 1000
*/
uid?: number;
/**
* The group ID associated with the file.
* @default 1000
*/
gid?: number;
/**
* The modification time of the file, expressed as the number of milliseconds since the UNIX epoch.
* @default Date.now()
*/
mtime?: number;
/**
* The name of the user who owns the file.
* @default ""
*/
user?: string;
/**
* The name of the group that owns the file.
* @default ""
*/
group?: string;
}
interface ParseTarOptions {
/**
* A filter function that determines whether a file entry should be skipped or not.
*/
filter?: (file: ParsedTarFileItemMeta) => boolean;
/**
* If `true`, only the metadata of the files will be parsed, and the file data will be omitted for listing purposes.
*/
metaOnly?: boolean;
}
/**
* Parses a TAR file from a binary buffer and returns an array of {@link TarFileItem} objects.
*
* @param {ArrayBuffer | Uint8Array} data - The binary data of the TAR file.
* @returns {ParsedTarFileItem[]} An array of file items contained in the TAR file.
*/
declare function parseTar<_ = never, _Opts extends ParseTarOptions = ParseTarOptions, _ItemType extends ParsedTarFileItem | ParsedTarFileItemMeta = _Opts["metaOnly"] extends true ? ParsedTarFileItemMeta : ParsedTarFileItem>(data: ArrayBuffer | Uint8Array, opts?: _Opts): _ItemType[];
/**
* Decompresses a gzipped TAR file and parses it to produce an array of file elements.
* This function handles the decompression of the gzip format before parsing the contents of the TAR.
*
* @param {ArrayBuffer | Uint8Array} data - The binary data of the gzipped TAR file.
* @param {object} opts - Decompression options.
* @param {CompressionFormat} [opts.compression="gzip"] - Specifies the compression format to use, defaults to `"gzip"`.
* @returns {Promise<TarFileItem[]>} A promise that resolves to an array of file items as described by {@link TarFileItem}.
*/
declare function parseTarGzip(data: ArrayBuffer | Uint8Array, opts?: ParseTarOptions & {
compression?: CompressionFormat;
}): Promise<ParsedTarFileItem[]>;
interface CreateTarOptions {
/**
* Default attributes applied to all file unless overridden. See {@link TarFileAttrs}.
* @optional
*/
attrs?: TarFileAttrs;
}
type TarFileInput = TarFileItem<string | Uint8Array | ArrayBuffer>;
/**
* Creates a TAR file from a list of file inputs and options, returning the TAR file as an `Uint8Array`.
* This function takes care of normalising the file data, setting default attributes and calculating the TAR structure.
*
* @param {TarFileInput[]} files - An array of files to include in the TAR archive. Each file can contain different data types. See {@link TarFileInput}.
* @param {CreateTarOptions} opts - File creation configuration options, including default file attributes. See {@link CreateTarOptions}.
* @returns {Uint8Array} The TAR file encoded as an `Uint8Array`.
*/
declare function createTar(files: TarFileInput[], opts?: CreateTarOptions): Uint8Array;
/**
* Creates a gzipped TAR file stream from an array of file inputs, using optional compression settings.
*
* @param {TarFileInput[]} files - The files to include in the gzipped TAR archive. See {@link TarFileInput}.
* @param {CreateTarOptions & { Compression? CompressionFormat }} opts - Options for TAR creation and gzip compression. See {@link CreateTarOptions}.
* @returns {ReadableStream} A stream of the gzipped TAR file data.
*/
declare function createTarGzipStream(files: TarFileInput[], opts?: CreateTarOptions & {
compression?: CompressionFormat;
}): ReadableStream;
/**
* Asynchronously creates a gzipped TAR file from an array of file inputs.
* This function is suitable for scenarios where a complete gzipped TAR file is required as a single `Uint8` array.
*
* @param {TarFileInput[]} files - The files to include in the gzipped TAR archive.
* @param {CreateTarOptions & { Compression? CompressionFormat }} opts - Options for TAR creation and gzip compression.
* @returns {Promise<Uint8Array>} A promise that resolves to the gzipped TAR file as an Uint8Array.
*/
declare function createTarGzip(files: TarFileInput[], opts?: CreateTarOptions & {
compression?: CompressionFormat;
}): Promise<Uint8Array>;
export { type CreateTarOptions, type ParseTarOptions, type ParsedTarFileItem, type ParsedTarFileItemMeta, type TarFileAttrs, type TarFileInput, type TarFileItem, createTar, createTarGzip, createTarGzipStream, parseTar, parseTarGzip };

133
node_modules/nanotar/dist/index.d.mts generated vendored Normal file
View File

@@ -0,0 +1,133 @@
type TarFileItem<DataT = Uint8Array> = {
/**
* File name
*/
name: string;
/**
* The data associated with the file. This field is usually omitted for directories.
* @optional
*/
data?: DataT;
/**
* The attributes of the file. See {@link TarFileAttrs}.
* @optional
*/
attrs?: TarFileAttrs;
};
interface ParsedTarFileItem extends TarFileItem {
/**
* The type of file system element. It can be `"file"`, `"directory"` or an operating system specific numeric code.
*/
type: "file" | "directory" | number;
/**
* The size of the file in bytes.
*/
size: number;
/**
* The textual representation of the file data. This property is read-only.
*/
readonly text: string;
}
type ParsedTarFileItemMeta = Omit<ParsedTarFileItem, "data" | "text">;
interface TarFileAttrs {
/**
* File mode in octal (e.g., `664`) represents read, write, and execute permissions for the owner, group, and others.
*/
mode?: string;
/**
* The user ID associated with the file.
* @default 1000
*/
uid?: number;
/**
* The group ID associated with the file.
* @default 1000
*/
gid?: number;
/**
* The modification time of the file, expressed as the number of milliseconds since the UNIX epoch.
* @default Date.now()
*/
mtime?: number;
/**
* The name of the user who owns the file.
* @default ""
*/
user?: string;
/**
* The name of the group that owns the file.
* @default ""
*/
group?: string;
}
interface ParseTarOptions {
/**
* A filter function that determines whether a file entry should be skipped or not.
*/
filter?: (file: ParsedTarFileItemMeta) => boolean;
/**
* If `true`, only the metadata of the files will be parsed, and the file data will be omitted for listing purposes.
*/
metaOnly?: boolean;
}
/**
* Parses a TAR file from a binary buffer and returns an array of {@link TarFileItem} objects.
*
* @param {ArrayBuffer | Uint8Array} data - The binary data of the TAR file.
* @returns {ParsedTarFileItem[]} An array of file items contained in the TAR file.
*/
declare function parseTar<_ = never, _Opts extends ParseTarOptions = ParseTarOptions, _ItemType extends ParsedTarFileItem | ParsedTarFileItemMeta = _Opts["metaOnly"] extends true ? ParsedTarFileItemMeta : ParsedTarFileItem>(data: ArrayBuffer | Uint8Array, opts?: _Opts): _ItemType[];
/**
* Decompresses a gzipped TAR file and parses it to produce an array of file elements.
* This function handles the decompression of the gzip format before parsing the contents of the TAR.
*
* @param {ArrayBuffer | Uint8Array} data - The binary data of the gzipped TAR file.
* @param {object} opts - Decompression options.
* @param {CompressionFormat} [opts.compression="gzip"] - Specifies the compression format to use, defaults to `"gzip"`.
* @returns {Promise<TarFileItem[]>} A promise that resolves to an array of file items as described by {@link TarFileItem}.
*/
declare function parseTarGzip(data: ArrayBuffer | Uint8Array, opts?: ParseTarOptions & {
compression?: CompressionFormat;
}): Promise<ParsedTarFileItem[]>;
interface CreateTarOptions {
/**
* Default attributes applied to all file unless overridden. See {@link TarFileAttrs}.
* @optional
*/
attrs?: TarFileAttrs;
}
type TarFileInput = TarFileItem<string | Uint8Array | ArrayBuffer>;
/**
* Creates a TAR file from a list of file inputs and options, returning the TAR file as an `Uint8Array`.
* This function takes care of normalising the file data, setting default attributes and calculating the TAR structure.
*
* @param {TarFileInput[]} files - An array of files to include in the TAR archive. Each file can contain different data types. See {@link TarFileInput}.
* @param {CreateTarOptions} opts - File creation configuration options, including default file attributes. See {@link CreateTarOptions}.
* @returns {Uint8Array} The TAR file encoded as an `Uint8Array`.
*/
declare function createTar(files: TarFileInput[], opts?: CreateTarOptions): Uint8Array;
/**
* Creates a gzipped TAR file stream from an array of file inputs, using optional compression settings.
*
* @param {TarFileInput[]} files - The files to include in the gzipped TAR archive. See {@link TarFileInput}.
* @param {CreateTarOptions & { Compression? CompressionFormat }} opts - Options for TAR creation and gzip compression. See {@link CreateTarOptions}.
* @returns {ReadableStream} A stream of the gzipped TAR file data.
*/
declare function createTarGzipStream(files: TarFileInput[], opts?: CreateTarOptions & {
compression?: CompressionFormat;
}): ReadableStream;
/**
* Asynchronously creates a gzipped TAR file from an array of file inputs.
* This function is suitable for scenarios where a complete gzipped TAR file is required as a single `Uint8` array.
*
* @param {TarFileInput[]} files - The files to include in the gzipped TAR archive.
* @param {CreateTarOptions & { Compression? CompressionFormat }} opts - Options for TAR creation and gzip compression.
* @returns {Promise<Uint8Array>} A promise that resolves to the gzipped TAR file as an Uint8Array.
*/
declare function createTarGzip(files: TarFileInput[], opts?: CreateTarOptions & {
compression?: CompressionFormat;
}): Promise<Uint8Array>;
export { type CreateTarOptions, type ParseTarOptions, type ParsedTarFileItem, type ParsedTarFileItemMeta, type TarFileAttrs, type TarFileInput, type TarFileItem, createTar, createTarGzip, createTarGzipStream, parseTar, parseTarGzip };

133
node_modules/nanotar/dist/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,133 @@
type TarFileItem<DataT = Uint8Array> = {
/**
* File name
*/
name: string;
/**
* The data associated with the file. This field is usually omitted for directories.
* @optional
*/
data?: DataT;
/**
* The attributes of the file. See {@link TarFileAttrs}.
* @optional
*/
attrs?: TarFileAttrs;
};
interface ParsedTarFileItem extends TarFileItem {
/**
* The type of file system element. It can be `"file"`, `"directory"` or an operating system specific numeric code.
*/
type: "file" | "directory" | number;
/**
* The size of the file in bytes.
*/
size: number;
/**
* The textual representation of the file data. This property is read-only.
*/
readonly text: string;
}
type ParsedTarFileItemMeta = Omit<ParsedTarFileItem, "data" | "text">;
interface TarFileAttrs {
/**
* File mode in octal (e.g., `664`) represents read, write, and execute permissions for the owner, group, and others.
*/
mode?: string;
/**
* The user ID associated with the file.
* @default 1000
*/
uid?: number;
/**
* The group ID associated with the file.
* @default 1000
*/
gid?: number;
/**
* The modification time of the file, expressed as the number of milliseconds since the UNIX epoch.
* @default Date.now()
*/
mtime?: number;
/**
* The name of the user who owns the file.
* @default ""
*/
user?: string;
/**
* The name of the group that owns the file.
* @default ""
*/
group?: string;
}
interface ParseTarOptions {
/**
* A filter function that determines whether a file entry should be skipped or not.
*/
filter?: (file: ParsedTarFileItemMeta) => boolean;
/**
* If `true`, only the metadata of the files will be parsed, and the file data will be omitted for listing purposes.
*/
metaOnly?: boolean;
}
/**
* Parses a TAR file from a binary buffer and returns an array of {@link TarFileItem} objects.
*
* @param {ArrayBuffer | Uint8Array} data - The binary data of the TAR file.
* @returns {ParsedTarFileItem[]} An array of file items contained in the TAR file.
*/
declare function parseTar<_ = never, _Opts extends ParseTarOptions = ParseTarOptions, _ItemType extends ParsedTarFileItem | ParsedTarFileItemMeta = _Opts["metaOnly"] extends true ? ParsedTarFileItemMeta : ParsedTarFileItem>(data: ArrayBuffer | Uint8Array, opts?: _Opts): _ItemType[];
/**
* Decompresses a gzipped TAR file and parses it to produce an array of file elements.
* This function handles the decompression of the gzip format before parsing the contents of the TAR.
*
* @param {ArrayBuffer | Uint8Array} data - The binary data of the gzipped TAR file.
* @param {object} opts - Decompression options.
* @param {CompressionFormat} [opts.compression="gzip"] - Specifies the compression format to use, defaults to `"gzip"`.
* @returns {Promise<TarFileItem[]>} A promise that resolves to an array of file items as described by {@link TarFileItem}.
*/
declare function parseTarGzip(data: ArrayBuffer | Uint8Array, opts?: ParseTarOptions & {
compression?: CompressionFormat;
}): Promise<ParsedTarFileItem[]>;
interface CreateTarOptions {
/**
* Default attributes applied to all file unless overridden. See {@link TarFileAttrs}.
* @optional
*/
attrs?: TarFileAttrs;
}
type TarFileInput = TarFileItem<string | Uint8Array | ArrayBuffer>;
/**
* Creates a TAR file from a list of file inputs and options, returning the TAR file as an `Uint8Array`.
* This function takes care of normalising the file data, setting default attributes and calculating the TAR structure.
*
* @param {TarFileInput[]} files - An array of files to include in the TAR archive. Each file can contain different data types. See {@link TarFileInput}.
* @param {CreateTarOptions} opts - File creation configuration options, including default file attributes. See {@link CreateTarOptions}.
* @returns {Uint8Array} The TAR file encoded as an `Uint8Array`.
*/
declare function createTar(files: TarFileInput[], opts?: CreateTarOptions): Uint8Array;
/**
* Creates a gzipped TAR file stream from an array of file inputs, using optional compression settings.
*
* @param {TarFileInput[]} files - The files to include in the gzipped TAR archive. See {@link TarFileInput}.
* @param {CreateTarOptions & { Compression? CompressionFormat }} opts - Options for TAR creation and gzip compression. See {@link CreateTarOptions}.
* @returns {ReadableStream} A stream of the gzipped TAR file data.
*/
declare function createTarGzipStream(files: TarFileInput[], opts?: CreateTarOptions & {
compression?: CompressionFormat;
}): ReadableStream;
/**
* Asynchronously creates a gzipped TAR file from an array of file inputs.
* This function is suitable for scenarios where a complete gzipped TAR file is required as a single `Uint8` array.
*
* @param {TarFileInput[]} files - The files to include in the gzipped TAR archive.
* @param {CreateTarOptions & { Compression? CompressionFormat }} opts - Options for TAR creation and gzip compression.
* @returns {Promise<Uint8Array>} A promise that resolves to the gzipped TAR file as an Uint8Array.
*/
declare function createTarGzip(files: TarFileInput[], opts?: CreateTarOptions & {
compression?: CompressionFormat;
}): Promise<Uint8Array>;
export { type CreateTarOptions, type ParseTarOptions, type ParsedTarFileItem, type ParsedTarFileItemMeta, type TarFileAttrs, type TarFileInput, type TarFileItem, createTar, createTarGzip, createTarGzipStream, parseTar, parseTarGzip };

223
node_modules/nanotar/dist/index.mjs generated vendored Normal file
View File

@@ -0,0 +1,223 @@
const TAR_TYPE_FILE = 0;
const TAR_TYPE_DIR = 5;
function parseTar(data, opts) {
const buffer = data.buffer || data;
const files = [];
let offset = 0;
while (offset < buffer.byteLength - 512) {
let name = _readString(buffer, offset, 100);
if (name.length === 0) {
break;
}
const mode = _readString(buffer, offset + 100, 8).trim();
const uid = Number.parseInt(_readString(buffer, offset + 108, 8));
const gid = Number.parseInt(_readString(buffer, offset + 116, 8));
const size = _readNumber(buffer, offset + 124, 12);
const seek = 512 + 512 * Math.trunc(size / 512) + (size % 512 ? 512 : 0);
const mtime = _readNumber(buffer, offset + 136, 12);
const _type = _readNumber(buffer, offset + 156, 1);
const type = _type === TAR_TYPE_FILE ? "file" : _type === TAR_TYPE_DIR ? "directory" : _type;
const user = _readString(buffer, offset + 265, 32);
const group = _readString(buffer, offset + 297, 32);
name = _sanitizePath(name);
const meta = {
name,
type,
size,
attrs: {
mode,
uid,
gid,
mtime,
user,
group
}
};
if (opts?.filter && !opts.filter(meta)) {
offset += seek;
continue;
}
if (opts?.metaOnly) {
files.push(meta);
offset += seek;
continue;
}
const data2 = _type === TAR_TYPE_DIR ? undefined : new Uint8Array(buffer, offset + 512, size);
files.push({
...meta,
data: data2,
get text() {
return new TextDecoder().decode(this.data);
}
});
offset += seek;
}
return files;
}
async function parseTarGzip(data, opts = {}) {
const stream = new ReadableStream({
start(controller) {
controller.enqueue(new Uint8Array(data));
controller.close();
}
}).pipeThrough(new DecompressionStream(opts.compression ?? "gzip"));
const decompressedData = await new Response(stream).arrayBuffer();
return parseTar(decompressedData, opts);
}
function _sanitizePath(path) {
let normalized = path.replace(/\\/g, "/");
normalized = normalized.replace(/^[a-zA-Z]:\//, "");
normalized = normalized.replace(/^\/+/, "");
const hasLeadingDotSlash = normalized.startsWith("./");
const parts = normalized.split("/");
const resolved = [];
for (const part of parts) {
if (part === "..") {
resolved.pop();
} else if (part !== "." && part !== "") {
resolved.push(part);
}
}
let result = resolved.join("/");
if (hasLeadingDotSlash && !result.startsWith("./")) {
result = "./" + result;
}
if (path.endsWith("/") && !result.endsWith("/")) {
result += "/";
}
return result;
}
function _readString(buffer, offset, size) {
const view = new Uint8Array(buffer, offset, size);
const i = view.indexOf(0);
const td = new TextDecoder();
return td.decode(i === -1 ? view : view.slice(0, i));
}
function _readNumber(buffer, offset, size) {
const view = new Uint8Array(buffer, offset, size);
let str = "";
for (let i = 0; i < size; i++) {
str += String.fromCodePoint(view[i]);
}
return Number.parseInt(str, 8);
}
function createTar(files, opts = {}) {
const _files = files.map((file) => {
const data = _normalizeData(file.data);
return {
...file,
data,
size: data?.length || 0
};
});
let tarDataSize = 0;
for (let i = 0; i < files.length; i++) {
const size = _files[i].data?.length ?? 0;
tarDataSize += 512 + 512 * Math.trunc(size / 512);
if (size % 512) {
tarDataSize += 512;
}
}
let bufSize = 10240 * Math.trunc(tarDataSize / 10240);
if (tarDataSize % 10240) {
bufSize += 10240;
}
const buffer = new ArrayBuffer(bufSize);
let offset = 0;
for (const file of _files) {
const isDir = !file.data;
_writeString(buffer, file.name, offset, 100);
const mode = file.attrs?.mode ?? opts.attrs?.mode ?? (isDir ? "775" : "664");
_writeString(buffer, _leftPad(mode, 7), offset + 100, 8);
const uid = file.attrs?.uid ?? opts.attrs?.uid ?? 1e3;
_writeString(buffer, _leftPad(uid.toString(8), 7), offset + 108, 8);
const gid = file.attrs?.gid ?? opts.attrs?.gid ?? 1e3;
_writeString(buffer, _leftPad(gid.toString(8), 7), offset + 116, 8);
_writeString(buffer, _leftPad(file.size.toString(8), 11), offset + 124, 12);
const mtime = file.attrs?.mtime ?? opts.attrs?.mtime ?? Date.now();
_writeString(
buffer,
_leftPad(Math.trunc(mtime / 1e3).toString(8), 11),
offset + 136,
12
);
const type = isDir ? "5" : "0";
_writeString(buffer, type, offset + 156, 1);
_writeString(
buffer,
"ustar",
offset + 257,
6
/* magic string */
);
_writeString(
buffer,
"00",
offset + 263,
2
/* magic version */
);
const user = file.attrs?.user ?? opts.attrs?.user ?? "";
_writeString(buffer, user, offset + 265, 32);
const group = file.attrs?.group ?? opts.attrs?.group ?? "";
_writeString(buffer, group, offset + 297, 32);
_writeString(buffer, " ", offset + 148, 8);
const header = new Uint8Array(buffer, offset, 512);
let chksum = 0;
for (let i = 0; i < 512; i++) {
chksum += header[i];
}
_writeString(buffer, chksum.toString(8), offset + 148, 8);
if (!isDir) {
const destArray = new Uint8Array(buffer, offset + 512, file.size);
for (let byteIdx = 0; byteIdx < file.size; byteIdx++) {
destArray[byteIdx] = file.data[byteIdx];
}
offset += 512 * Math.trunc(file.size / 512);
if (file.size % 512) {
offset += 512;
}
}
offset += 512;
}
return new Uint8Array(buffer);
}
function createTarGzipStream(files, opts = {}) {
const buffer = createTar(files, opts);
return new ReadableStream({
start(controller) {
controller.enqueue(buffer);
controller.close();
}
}).pipeThrough(new CompressionStream(opts.compression ?? "gzip"));
}
async function createTarGzip(files, opts = {}) {
const data = await new Response(createTarGzipStream(files, opts)).arrayBuffer().then((buffer) => new Uint8Array(buffer));
return data;
}
function _writeString(buffer, str, offset, size) {
const strView = new Uint8Array(buffer, offset, size);
const te = new TextEncoder();
const written = te.encodeInto(str, strView).written;
for (let i = written; i < size; i++) {
strView[i] = 0;
}
}
function _leftPad(input, targetLength) {
return String(input).padStart(targetLength, "0");
}
function _normalizeData(data) {
if (data === null || data === undefined) {
return undefined;
}
if (typeof data === "string") {
return new TextEncoder().encode(data);
}
if (data instanceof ArrayBuffer) {
return new Uint8Array(data);
}
return data;
}
export { createTar, createTarGzip, createTarGzipStream, parseTar, parseTarGzip };