Initial commit - full huishou project

This commit is contained in:
jiapengyu
2026-07-27 14:07:26 +08:00
commit 60790ad1b3
39127 changed files with 5989265 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
Copyright (c) 2014 Chris Talkington, contributors.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
+25
View File
@@ -0,0 +1,25 @@
# Compress Commons
Compress Commons is a library that defines a common interface for working with archive formats within node.
[![NPM](https://nodei.co/npm/compress-commons.png)](https://nodei.co/npm/compress-commons/)
## Install
```bash
npm install compress-commons --save
```
You can also use `npm install https://github.com/archiverjs/node-compress-commons/archive/master.tar.gz` to test upcoming versions.
## Things of Interest
- [Changelog](https://github.com/archiverjs/node-compress-commons/releases)
- [Contributing](https://github.com/archiverjs/node-compress-commons/blob/master/CONTRIBUTING.md)
- [MIT License](https://github.com/archiverjs/node-compress-commons/blob/master/LICENSE-MIT)
## Credits
Concept inspired by [Apache Commons Compress](http://commons.apache.org/proper/commons-compress/)™.
Some logic derived from [Apache Commons Compress](http://commons.apache.org/proper/commons-compress/)™ and [OpenJDK 7](http://openjdk.java.net/).
+6
View File
@@ -0,0 +1,6 @@
export default class ArchiveEntry {
getName() {}
getSize() {}
getLastModifiedDate() {}
isDirectory() {}
}
+98
View File
@@ -0,0 +1,98 @@
import { inherits } from "util";
import { isStream } from "is-stream";
import { Transform } from "readable-stream";
import ArchiveEntry from "./archive-entry.js";
import { normalizeInputSource } from "../util/index.js";
export default class ArchiveOutputStream extends Transform {
constructor(options) {
super(options);
this.offset = 0;
this._archive = {
finish: false,
finished: false,
processing: false,
};
}
_appendBuffer(zae, source, callback) {
// scaffold only
}
_appendStream(zae, source, callback) {
// scaffold only
}
_emitErrorCallback = function (err) {
if (err) {
this.emit("error", err);
}
};
_finish(ae) {
// scaffold only
}
_normalizeEntry(ae) {
// scaffold only
}
_transform(chunk, encoding, callback) {
callback(null, chunk);
}
entry(ae, source, callback) {
source = source || null;
if (typeof callback !== "function") {
callback = this._emitErrorCallback.bind(this);
}
if (!(ae instanceof ArchiveEntry)) {
callback(new Error("not a valid instance of ArchiveEntry"));
return;
}
if (this._archive.finish || this._archive.finished) {
callback(new Error("unacceptable entry after finish"));
return;
}
if (this._archive.processing) {
callback(new Error("already processing an entry"));
return;
}
this._archive.processing = true;
this._normalizeEntry(ae);
this._entry = ae;
source = normalizeInputSource(source);
if (Buffer.isBuffer(source)) {
this._appendBuffer(ae, source, callback);
} else if (isStream(source)) {
this._appendStream(ae, source, callback);
} else {
this._archive.processing = false;
callback(
new Error("input source must be valid Stream or Buffer instance"),
);
return;
}
return this;
}
finish() {
if (this._archive.processing) {
this._archive.finish = true;
return;
}
this._finish();
}
getBytesWritten() {
return this.offset;
}
write(chunk, cb) {
if (chunk) {
this.offset += chunk.length;
}
return super.write(chunk, cb);
}
}
+100
View File
@@ -0,0 +1,100 @@
export const WORD = 4;
export const DWORD = 8;
export const EMPTY = Buffer.alloc(0);
export const SHORT = 2;
export const SHORT_MASK = 0xffff;
export const SHORT_SHIFT = 16;
export const SHORT_ZERO = Buffer.from(Array(2));
export const LONG = 4;
export const LONG_ZERO = Buffer.from(Array(4));
export const MIN_VERSION_INITIAL = 10;
export const MIN_VERSION_DATA_DESCRIPTOR = 20;
export const MIN_VERSION_ZIP64 = 45;
export const VERSION_MADEBY = 45;
export const METHOD_STORED = 0;
export const METHOD_DEFLATED = 8;
export const PLATFORM_UNIX = 3;
export const PLATFORM_FAT = 0;
export const SIG_LFH = 0x04034b50;
export const SIG_DD = 0x08074b50;
export const SIG_CFH = 0x02014b50;
export const SIG_EOCD = 0x06054b50;
export const SIG_ZIP64_EOCD = 0x06064b50;
export const SIG_ZIP64_EOCD_LOC = 0x07064b50;
export const ZIP64_MAGIC_SHORT = 0xffff;
export const ZIP64_MAGIC = 0xffffffff;
export const ZIP64_EXTRA_ID = 0x0001;
export const ZLIB_NO_COMPRESSION = 0;
export const ZLIB_BEST_SPEED = 1;
export const ZLIB_BEST_COMPRESSION = 9;
export const ZLIB_DEFAULT_COMPRESSION = -1;
export const MODE_MASK = 0xfff;
export const DEFAULT_FILE_MODE = 33188;
export const DEFAULT_DIR_MODE = 16877;
export const EXT_FILE_ATTR_DIR = 1106051088;
export const EXT_FILE_ATTR_FILE = 2175008800;
export const S_IFMT = 61440;
export const S_IFIFO = 4096;
export const S_IFCHR = 8192;
export const S_IFDIR = 16384;
export const S_IFBLK = 24576;
export const S_IFREG = 32768;
export const S_IFLNK = 40960;
export const S_IFSOCK = 49152;
export const S_DOS_A = 32;
export const S_DOS_D = 16;
export const S_DOS_V = 8;
export const S_DOS_S = 4;
export const S_DOS_H = 2;
export const S_DOS_R = 1; // 01 Read Only
export default {
WORD,
DWORD,
EMPTY,
SHORT,
SHORT_MASK,
SHORT_SHIFT,
SHORT_ZERO,
LONG,
LONG_ZERO,
MIN_VERSION_INITIAL,
MIN_VERSION_DATA_DESCRIPTOR,
MIN_VERSION_ZIP64,
VERSION_MADEBY,
METHOD_STORED,
METHOD_DEFLATED,
PLATFORM_UNIX,
PLATFORM_FAT,
SIG_LFH,
SIG_DD,
SIG_CFH,
SIG_EOCD,
SIG_ZIP64_EOCD,
SIG_ZIP64_EOCD_LOC,
ZIP64_MAGIC_SHORT,
ZIP64_MAGIC,
ZIP64_EXTRA_ID,
ZLIB_NO_COMPRESSION,
ZLIB_BEST_SPEED,
ZLIB_BEST_COMPRESSION,
ZLIB_DEFAULT_COMPRESSION,
MODE_MASK,
DEFAULT_FILE_MODE,
DEFAULT_DIR_MODE,
EXT_FILE_ATTR_DIR,
EXT_FILE_ATTR_FILE,
S_IFMT,
S_IFIFO,
S_IFCHR,
S_IFDIR,
S_IFBLK,
S_IFREG,
S_IFLNK,
S_IFSOCK,
S_DOS_A,
S_DOS_D,
S_DOS_V,
S_DOS_S,
S_DOS_H,
S_DOS_R,
};
+79
View File
@@ -0,0 +1,79 @@
import { getShortBytes, getShortBytesValue } from "./util.js";
var DATA_DESCRIPTOR_FLAG = 1 << 3;
var ENCRYPTION_FLAG = 1 << 0;
var NUMBER_OF_SHANNON_FANO_TREES_FLAG = 1 << 2;
var SLIDING_DICTIONARY_SIZE_FLAG = 1 << 1;
var STRONG_ENCRYPTION_FLAG = 1 << 6;
var UFT8_NAMES_FLAG = 1 << 11;
export default class GeneralPurposeBit {
constructor() {
this.descriptor = false;
this.encryption = false;
this.utf8 = false;
this.numberOfShannonFanoTrees = 0;
this.strongEncryption = false;
this.slidingDictionarySize = 0;
return this;
}
encode() {
return getShortBytes(
(this.descriptor ? DATA_DESCRIPTOR_FLAG : 0) |
(this.utf8 ? UFT8_NAMES_FLAG : 0) |
(this.encryption ? ENCRYPTION_FLAG : 0) |
(this.strongEncryption ? STRONG_ENCRYPTION_FLAG : 0),
);
}
static parse(buf, offset) {
var flag = getShortBytesValue(buf, offset);
var gbp = new GeneralPurposeBit();
gbp.useDataDescriptor((flag & DATA_DESCRIPTOR_FLAG) !== 0);
gbp.useUTF8ForNames((flag & UFT8_NAMES_FLAG) !== 0);
gbp.useStrongEncryption((flag & STRONG_ENCRYPTION_FLAG) !== 0);
gbp.useEncryption((flag & ENCRYPTION_FLAG) !== 0);
gbp.setSlidingDictionarySize(
(flag & SLIDING_DICTIONARY_SIZE_FLAG) !== 0 ? 8192 : 4096,
);
gbp.setNumberOfShannonFanoTrees(
(flag & NUMBER_OF_SHANNON_FANO_TREES_FLAG) !== 0 ? 3 : 2,
);
return gbp;
}
setNumberOfShannonFanoTrees(n) {
this.numberOfShannonFanoTrees = n;
}
getNumberOfShannonFanoTrees() {
return this.numberOfShannonFanoTrees;
}
setSlidingDictionarySize(n) {
this.slidingDictionarySize = n;
}
getSlidingDictionarySize() {
return this.slidingDictionarySize;
}
useDataDescriptor(b) {
this.descriptor = b;
}
usesDataDescriptor() {
return this.descriptor;
}
useEncryption(b) {
this.encryption = b;
}
usesEncryption() {
return this.encryption;
}
useStrongEncryption(b) {
this.strongEncryption = b;
}
usesStrongEncryption() {
return this.strongEncryption;
}
useUTF8ForNames(b) {
this.utf8 = b;
}
usesUTF8ForNames() {
return this.utf8;
}
}
+19
View File
@@ -0,0 +1,19 @@
export const PERM_MASK = 4095;
export const FILE_TYPE_FLAG = 61440;
export const LINK_FLAG = 40960;
export const FILE_FLAG = 32768;
export const DIR_FLAG = 16384;
export const DEFAULT_LINK_PERM = 511;
export const DEFAULT_DIR_PERM = 493;
export const DEFAULT_FILE_PERM = 420; // 0644
export default {
PERM_MASK,
FILE_TYPE_FLAG,
LINK_FLAG,
FILE_FLAG,
DIR_FLAG,
DEFAULT_LINK_PERM,
DEFAULT_DIR_PERM,
DEFAULT_FILE_PERM,
};
+74
View File
@@ -0,0 +1,74 @@
export function dateToDos(d, forceLocalTime) {
forceLocalTime = forceLocalTime || false;
var year = forceLocalTime ? d.getFullYear() : d.getUTCFullYear();
if (year < 1980) {
return 2162688; // 1980-1-1 00:00:00
} else if (year >= 2044) {
return 2141175677; // 2043-12-31 23:59:58
}
var val = {
year: year,
month: forceLocalTime ? d.getMonth() : d.getUTCMonth(),
date: forceLocalTime ? d.getDate() : d.getUTCDate(),
hours: forceLocalTime ? d.getHours() : d.getUTCHours(),
minutes: forceLocalTime ? d.getMinutes() : d.getUTCMinutes(),
seconds: forceLocalTime ? d.getSeconds() : d.getUTCSeconds(),
};
return (
((val.year - 1980) << 25) |
((val.month + 1) << 21) |
(val.date << 16) |
(val.hours << 11) |
(val.minutes << 5) |
(val.seconds / 2)
);
}
export function dosToDate(dos) {
return new Date(
((dos >> 25) & 0x7f) + 1980,
((dos >> 21) & 0x0f) - 1,
(dos >> 16) & 0x1f,
(dos >> 11) & 0x1f,
(dos >> 5) & 0x3f,
(dos & 0x1f) << 1,
);
}
export function fromDosTime(buf) {
return dosToDate(buf.readUInt32LE(0));
}
export function getEightBytes(v) {
var buf = Buffer.alloc(8);
buf.writeUInt32LE(v % 0x0100000000, 0);
buf.writeUInt32LE((v / 0x0100000000) | 0, 4);
return buf;
}
export function getShortBytes(v) {
var buf = Buffer.alloc(2);
buf.writeUInt16LE((v & 0xffff) >>> 0, 0);
return buf;
}
export function getShortBytesValue(buf, offset) {
return buf.readUInt16LE(offset);
}
export function getLongBytes(v) {
var buf = Buffer.alloc(4);
buf.writeUInt32LE((v & 0xffffffff) >>> 0, 0);
return buf;
}
export function getLongBytesValue(buf, offset) {
return buf.readUInt32LE(offset);
}
export function toDosTime(d) {
return getLongBytes(util.dateToDos(d));
}
export default {
dateToDos,
dosToDate,
fromDosTime,
getEightBytes,
getShortBytes,
getShortBytesValue,
getLongBytes,
getLongBytesValue,
toDosTime,
};
+380
View File
@@ -0,0 +1,380 @@
import { inherits } from "util";
import normalizePath from "normalize-path";
import ArchiveEntry from "../archive-entry.js";
import GeneralPurposeBit from "./general-purpose-bit.js";
import UnixStat from "./unix-stat.js";
import {
EMPTY,
MIN_VERSION_INITIAL,
MODE_MASK,
PLATFORM_FAT,
PLATFORM_UNIX,
S_DOS_A,
S_DOS_D,
S_IFDIR,
S_IFREG,
SHORT_MASK,
SHORT_SHIFT,
ZIP64_MAGIC,
} from "./constants.js";
import { dateToDos, dosToDate } from "./util.js";
export default class ZipArchiveEntry extends ArchiveEntry {
constructor(name) {
super();
this.platform = PLATFORM_FAT;
this.method = -1;
this.name = null;
this.size = 0;
this.csize = 0;
this.gpb = new GeneralPurposeBit();
this.crc = 0;
this.time = -1;
this.minver = MIN_VERSION_INITIAL;
this.mode = -1;
this.extra = null;
this.exattr = 0;
this.inattr = 0;
this.comment = null;
if (name) {
this.setName(name);
}
}
/**
* Returns the extra fields related to the entry.
*
* @returns {Buffer}
*/
getCentralDirectoryExtra() {
return this.getExtra();
}
/**
* Returns the comment set for the entry.
*
* @returns {string}
*/
getComment() {
return this.comment !== null ? this.comment : "";
}
/**
* Returns the compressed size of the entry.
*
* @returns {number}
*/
getCompressedSize() {
return this.csize;
}
/**
* Returns the CRC32 digest for the entry.
*
* @returns {number}
*/
getCrc() {
return this.crc;
}
/**
* Returns the external file attributes for the entry.
*
* @returns {number}
*/
getExternalAttributes = function () {
return this.exattr;
};
/**
* Returns the extra fields related to the entry.
*
* @returns {Buffer}
*/
getExtra() {
return this.extra !== null ? this.extra : EMPTY;
}
/**
* Returns the general purpose bits related to the entry.
*
* @returns {GeneralPurposeBit}
*/
getGeneralPurposeBit() {
return this.gpb;
}
/**
* Returns the internal file attributes for the entry.
*
* @returns {number}
*/
getInternalAttributes() {
return this.inattr;
}
/**
* Returns the last modified date of the entry.
*
* @returns {number}
*/
getLastModifiedDate() {
return this.getTime();
}
/**
* Returns the extra fields related to the entry.
*
* @returns {Buffer}
*/
getLocalFileDataExtra() {
return this.getExtra();
}
/**
* Returns the compression method used on the entry.
*
* @returns {number}
*/
getMethod() {
return this.method;
}
/**
* Returns the filename of the entry.
*
* @returns {string}
*/
getName() {
return this.name;
}
/**
* Returns the platform on which the entry was made.
*
* @returns {number}
*/
getPlatform() {
return this.platform;
}
/**
* Returns the size of the entry.
*
* @returns {number}
*/
getSize() {
return this.size;
}
/**
* Returns a date object representing the last modified date of the entry.
*
* @returns {number|Date}
*/
getTime() {
return this.time !== -1 ? dosToDate(this.time) : -1;
}
/**
* Returns the DOS timestamp for the entry.
*
* @returns {number}
*/
getTimeDos() {
return this.time !== -1 ? this.time : 0;
}
/**
* Returns the UNIX file permissions for the entry.
*
* @returns {number}
*/
getUnixMode() {
return this.platform !== PLATFORM_UNIX
? 0
: (this.getExternalAttributes() >> SHORT_SHIFT) & SHORT_MASK;
}
/**
* Returns the version of ZIP needed to extract the entry.
*
* @returns {number}
*/
getVersionNeededToExtract() {
return this.minver;
}
/**
* Sets the comment of the entry.
*
* @param comment
*/
setComment(comment) {
if (Buffer.byteLength(comment) !== comment.length) {
this.getGeneralPurposeBit().useUTF8ForNames(true);
}
this.comment = comment;
}
/**
* Sets the compressed size of the entry.
*
* @param size
*/
setCompressedSize(size) {
if (size < 0) {
throw new Error("invalid entry compressed size");
}
this.csize = size;
}
/**
* Sets the checksum of the entry.
*
* @param crc
*/
setCrc(crc) {
if (crc < 0) {
throw new Error("invalid entry crc32");
}
this.crc = crc;
}
/**
* Sets the external file attributes of the entry.
*
* @param attr
*/
setExternalAttributes(attr) {
this.exattr = attr >>> 0;
}
/**
* Sets the extra fields related to the entry.
*
* @param extra
*/
setExtra(extra) {
this.extra = extra;
}
/**
* Sets the general purpose bits related to the entry.
*
* @param gpb
*/
setGeneralPurposeBit(gpb) {
if (!(gpb instanceof GeneralPurposeBit)) {
throw new Error("invalid entry GeneralPurposeBit");
}
this.gpb = gpb;
}
/**
* Sets the internal file attributes of the entry.
*
* @param attr
*/
setInternalAttributes(attr) {
this.inattr = attr;
}
/**
* Sets the compression method of the entry.
*
* @param method
*/
setMethod(method) {
if (method < 0) {
throw new Error("invalid entry compression method");
}
this.method = method;
}
/**
* Sets the name of the entry.
*
* @param name
* @param prependSlash
*/
setName(name, prependSlash = false) {
name = normalizePath(name, false)
.replace(/^\w+:/, "")
.replace(/^(\.\.\/|\/)+/, "");
if (prependSlash) {
name = `/${name}`;
}
if (Buffer.byteLength(name) !== name.length) {
this.getGeneralPurposeBit().useUTF8ForNames(true);
}
this.name = name;
}
/**
* Sets the platform on which the entry was made.
*
* @param platform
*/
setPlatform(platform) {
this.platform = platform;
}
/**
* Sets the size of the entry.
*
* @param size
*/
setSize(size) {
if (size < 0) {
throw new Error("invalid entry size");
}
this.size = size;
}
/**
* Sets the time of the entry.
*
* @param time
* @param forceLocalTime
*/
setTime(time, forceLocalTime) {
if (!(time instanceof Date)) {
throw new Error("invalid entry time");
}
this.time = dateToDos(time, forceLocalTime);
}
/**
* Sets the UNIX file permissions for the entry.
*
* @param mode
*/
setUnixMode(mode) {
mode |= this.isDirectory() ? S_IFDIR : S_IFREG;
var extattr = 0;
extattr |= (mode << SHORT_SHIFT) | (this.isDirectory() ? S_DOS_D : S_DOS_A);
this.setExternalAttributes(extattr);
this.mode = mode & MODE_MASK;
this.platform = PLATFORM_UNIX;
}
/**
* Sets the version of ZIP needed to extract this entry.
*
* @param minver
*/
setVersionNeededToExtract(minver) {
this.minver = minver;
}
/**
* Returns true if this entry represents a directory.
*
* @returns {boolean}
*/
isDirectory() {
return this.getName().slice(-1) === "/";
}
/**
* Returns true if this entry represents a unix symlink,
* in which case the entry's content contains the target path
* for the symlink.
*
* @returns {boolean}
*/
isUnixSymlink() {
return (
(this.getUnixMode() & UnixStat.FILE_TYPE_FLAG) === UnixStat.LINK_FLAG
);
}
/**
* Returns true if this entry is using the ZIP64 extension of ZIP.
*
* @returns {boolean}
*/
isZip64() {
return this.csize > ZIP64_MAGIC || this.size > ZIP64_MAGIC;
}
}
@@ -0,0 +1,371 @@
import { inherits } from "util";
import crc32 from "crc-32";
import { CRC32Stream, DeflateCRC32Stream } from "crc32-stream";
import ArchiveOutputStream from "../archive-output-stream.js";
import ZipArchiveEntry from "./zip-archive-entry.js";
import GeneralPurposeBit from "./general-purpose-bit.js";
import {
LONG_ZERO,
METHOD_DEFLATED,
METHOD_STORED,
MIN_VERSION_DATA_DESCRIPTOR,
MIN_VERSION_ZIP64,
SHORT_ZERO,
SIG_EOCD,
SIG_DD,
SIG_CFH,
SIG_LFH,
SIG_ZIP64_EOCD,
SIG_ZIP64_EOCD_LOC,
VERSION_MADEBY,
ZIP64_EXTRA_ID,
ZIP64_MAGIC,
ZIP64_MAGIC_SHORT,
ZLIB_BEST_SPEED,
} from "./constants.js";
import { getEightBytes, getLongBytes, getShortBytes } from "./util.js";
function _defaults(o) {
if (typeof o !== "object") {
o = {};
}
if (typeof o.zlib !== "object") {
o.zlib = {};
}
if (typeof o.zlib.level !== "number") {
o.zlib.level = ZLIB_BEST_SPEED;
}
o.forceZip64 = !!o.forceZip64;
o.forceLocalTime = !!o.forceLocalTime;
return o;
}
export default class ZipArchiveOutputStream extends ArchiveOutputStream {
constructor(options) {
const _options = _defaults(options);
super(_options);
this.options = _options;
this._entry = null;
this._entries = [];
this._archive = {
centralLength: 0,
centralOffset: 0,
comment: "",
finish: false,
finished: false,
processing: false,
forceZip64: _options.forceZip64,
forceLocalTime: _options.forceLocalTime,
};
}
_afterAppend(ae) {
this._entries.push(ae);
if (ae.getGeneralPurposeBit().usesDataDescriptor()) {
this._writeDataDescriptor(ae);
}
this._archive.processing = false;
this._entry = null;
if (this._archive.finish && !this._archive.finished) {
this._finish();
}
}
_appendBuffer(ae, source, callback) {
if (source.length === 0) {
ae.setMethod(METHOD_STORED);
}
var method = ae.getMethod();
if (method === METHOD_STORED) {
ae.setSize(source.length);
ae.setCompressedSize(source.length);
ae.setCrc(crc32.buf(source) >>> 0);
}
this._writeLocalFileHeader(ae);
if (method === METHOD_STORED) {
this.write(source);
this._afterAppend(ae);
callback(null, ae);
return;
} else if (method === METHOD_DEFLATED) {
this._smartStream(ae, callback).end(source);
return;
} else {
callback(new Error("compression method " + method + " not implemented"));
return;
}
}
_appendStream(ae, source, callback) {
ae.getGeneralPurposeBit().useDataDescriptor(true);
ae.setVersionNeededToExtract(MIN_VERSION_DATA_DESCRIPTOR);
this._writeLocalFileHeader(ae);
var smart = this._smartStream(ae, callback);
source.once("error", function (err) {
smart.emit("error", err);
smart.end();
});
source.pipe(smart);
}
_finish() {
this._archive.centralOffset = this.offset;
this._entries.forEach(
function (ae) {
this._writeCentralFileHeader(ae);
}.bind(this),
);
this._archive.centralLength = this.offset - this._archive.centralOffset;
if (this.isZip64()) {
this._writeCentralDirectoryZip64();
}
this._writeCentralDirectoryEnd();
this._archive.processing = false;
this._archive.finish = true;
this._archive.finished = true;
this.end();
}
_normalizeEntry(ae) {
if (ae.getMethod() === -1) {
ae.setMethod(METHOD_DEFLATED);
}
if (ae.getMethod() === METHOD_DEFLATED) {
ae.getGeneralPurposeBit().useDataDescriptor(true);
ae.setVersionNeededToExtract(MIN_VERSION_DATA_DESCRIPTOR);
}
if (ae.getTime() === -1) {
ae.setTime(new Date(), this._archive.forceLocalTime);
}
ae._offsets = {
file: 0,
data: 0,
contents: 0,
};
}
_smartStream(ae, callback) {
var deflate = ae.getMethod() === METHOD_DEFLATED;
var process = deflate
? new DeflateCRC32Stream(this.options.zlib)
: new CRC32Stream();
var error = null;
function handleStuff() {
var digest = process.digest().readUInt32BE(0);
ae.setCrc(digest);
ae.setSize(process.size());
ae.setCompressedSize(process.size(true));
this._afterAppend(ae);
callback(error, ae);
}
process.once("end", handleStuff.bind(this));
process.once("error", function (err) {
error = err;
});
process.pipe(this, { end: false });
return process;
}
_writeCentralDirectoryEnd() {
var records = this._entries.length;
var size = this._archive.centralLength;
var offset = this._archive.centralOffset;
if (this.isZip64()) {
records = ZIP64_MAGIC_SHORT;
size = ZIP64_MAGIC;
offset = ZIP64_MAGIC;
}
// signature
this.write(getLongBytes(SIG_EOCD));
// disk numbers
this.write(SHORT_ZERO);
this.write(SHORT_ZERO);
// number of entries
this.write(getShortBytes(records));
this.write(getShortBytes(records));
// length and location of CD
this.write(getLongBytes(size));
this.write(getLongBytes(offset));
// archive comment
var comment = this.getComment();
var commentLength = Buffer.byteLength(comment);
this.write(getShortBytes(commentLength));
this.write(comment);
}
_writeCentralDirectoryZip64() {
// signature
this.write(getLongBytes(SIG_ZIP64_EOCD));
// size of the ZIP64 EOCD record
this.write(getEightBytes(44));
// version made by
this.write(getShortBytes(MIN_VERSION_ZIP64));
// version to extract
this.write(getShortBytes(MIN_VERSION_ZIP64));
// disk numbers
this.write(LONG_ZERO);
this.write(LONG_ZERO);
// number of entries
this.write(getEightBytes(this._entries.length));
this.write(getEightBytes(this._entries.length));
// length and location of CD
this.write(getEightBytes(this._archive.centralLength));
this.write(getEightBytes(this._archive.centralOffset));
// extensible data sector
// not implemented at this time
// end of central directory locator
this.write(getLongBytes(SIG_ZIP64_EOCD_LOC));
// disk number holding the ZIP64 EOCD record
this.write(LONG_ZERO);
// relative offset of the ZIP64 EOCD record
this.write(
getEightBytes(this._archive.centralOffset + this._archive.centralLength),
);
// total number of disks
this.write(getLongBytes(1));
}
_writeCentralFileHeader(ae) {
var gpb = ae.getGeneralPurposeBit();
var method = ae.getMethod();
var fileOffset = ae._offsets.file;
var size = ae.getSize();
var compressedSize = ae.getCompressedSize();
if (ae.isZip64() || fileOffset > ZIP64_MAGIC) {
size = ZIP64_MAGIC;
compressedSize = ZIP64_MAGIC;
fileOffset = ZIP64_MAGIC;
ae.setVersionNeededToExtract(MIN_VERSION_ZIP64);
var extraBuf = Buffer.concat(
[
getShortBytes(ZIP64_EXTRA_ID),
getShortBytes(24),
getEightBytes(ae.getSize()),
getEightBytes(ae.getCompressedSize()),
getEightBytes(ae._offsets.file),
],
28,
);
ae.setExtra(extraBuf);
}
// signature
this.write(getLongBytes(SIG_CFH));
// version made by
this.write(getShortBytes((ae.getPlatform() << 8) | VERSION_MADEBY));
// version to extract and general bit flag
this.write(getShortBytes(ae.getVersionNeededToExtract()));
this.write(gpb.encode());
// compression method
this.write(getShortBytes(method));
// datetime
this.write(getLongBytes(ae.getTimeDos()));
// crc32 checksum
this.write(getLongBytes(ae.getCrc()));
// sizes
this.write(getLongBytes(compressedSize));
this.write(getLongBytes(size));
var name = ae.getName();
var comment = ae.getComment();
var extra = ae.getCentralDirectoryExtra();
if (gpb.usesUTF8ForNames()) {
name = Buffer.from(name);
comment = Buffer.from(comment);
}
// name length
this.write(getShortBytes(name.length));
// extra length
this.write(getShortBytes(extra.length));
// comments length
this.write(getShortBytes(comment.length));
// disk number start
this.write(SHORT_ZERO);
// internal attributes
this.write(getShortBytes(ae.getInternalAttributes()));
// external attributes
this.write(getLongBytes(ae.getExternalAttributes()));
// relative offset of LFH
this.write(getLongBytes(fileOffset));
// name
this.write(name);
// extra
this.write(extra);
// comment
this.write(comment);
}
_writeDataDescriptor(ae) {
// signature
this.write(getLongBytes(SIG_DD));
// crc32 checksum
this.write(getLongBytes(ae.getCrc()));
// sizes
if (ae.isZip64()) {
this.write(getEightBytes(ae.getCompressedSize()));
this.write(getEightBytes(ae.getSize()));
} else {
this.write(getLongBytes(ae.getCompressedSize()));
this.write(getLongBytes(ae.getSize()));
}
}
_writeLocalFileHeader(ae) {
var gpb = ae.getGeneralPurposeBit();
var method = ae.getMethod();
var name = ae.getName();
var extra = ae.getLocalFileDataExtra();
if (ae.isZip64()) {
gpb.useDataDescriptor(true);
ae.setVersionNeededToExtract(MIN_VERSION_ZIP64);
}
if (gpb.usesUTF8ForNames()) {
name = Buffer.from(name);
}
ae._offsets.file = this.offset;
// signature
this.write(getLongBytes(SIG_LFH));
// version to extract and general bit flag
this.write(getShortBytes(ae.getVersionNeededToExtract()));
this.write(gpb.encode());
// compression method
this.write(getShortBytes(method));
// datetime
this.write(getLongBytes(ae.getTimeDos()));
ae._offsets.data = this.offset;
// crc32 checksum and sizes
if (gpb.usesDataDescriptor()) {
this.write(LONG_ZERO);
this.write(LONG_ZERO);
this.write(LONG_ZERO);
} else {
this.write(getLongBytes(ae.getCrc()));
this.write(getLongBytes(ae.getCompressedSize()));
this.write(getLongBytes(ae.getSize()));
}
// name length
this.write(getShortBytes(name.length));
// extra length
this.write(getShortBytes(extra.length));
// name
this.write(name);
// extra
this.write(extra);
ae._offsets.contents = this.offset;
}
getComment(comment) {
return this._archive.comment !== null ? this._archive.comment : "";
}
isZip64() {
return (
this._archive.forceZip64 ||
this._entries.length > ZIP64_MAGIC_SHORT ||
this._archive.centralLength > ZIP64_MAGIC ||
this._archive.centralOffset > ZIP64_MAGIC
);
}
setComment(comment) {
this._archive.comment = comment;
}
}
+18
View File
@@ -0,0 +1,18 @@
import ArchiveEntry from "./archivers/archive-entry.js";
import ZipArchiveEntry from "./archivers/zip/zip-archive-entry.js";
import ArchiveOutputStream from "./archivers/archive-output-stream.js";
import ZipArchiveOutputStream from "./archivers/zip/zip-archive-output-stream.js";
export {
ArchiveEntry,
ZipArchiveEntry,
ArchiveOutputStream,
ZipArchiveOutputStream,
};
export default {
ArchiveEntry,
ZipArchiveEntry,
ArchiveOutputStream,
ZipArchiveOutputStream,
};
+20
View File
@@ -0,0 +1,20 @@
import { Stream } from "stream";
import { PassThrough } from "readable-stream";
import { isStream } from "is-stream";
export function normalizeInputSource(source) {
if (source === null) {
return Buffer.alloc(0);
} else if (typeof source === "string") {
return Buffer.from(source);
} else if (isStream(source) && !source._readableState) {
var normalized = new PassThrough();
source.pipe(normalized);
return normalized;
}
return source;
}
export default {
normalizeInputSource,
};
+90
View File
@@ -0,0 +1,90 @@
import {
type Stream,
type Writable as WritableStream,
type Readable as ReadableStream,
type Duplex as DuplexStream,
type Transform as TransformStream,
} from 'node:stream';
export type Options = {
/**
When this option is `true`, the method returns `false` if the stream has already been closed.
@default true
*/
checkOpen?: boolean;
};
/**
@returns Whether `stream` is a [`Stream`](https://nodejs.org/api/stream.html#stream_stream).
@example
```
import fs from 'node:fs';
import {isStream} from 'is-stream';
isStream(fs.createReadStream('unicorn.png'));
//=> true
isStream({});
//=> false
```
*/
export function isStream(stream: unknown, options?: Options): stream is Stream;
/**
@returns Whether `stream` is a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable), an [`http.OutgoingMessage`](https://nodejs.org/api/http.html#class-httpoutgoingmessage), an [`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse) or an [`http.ClientRequest`](https://nodejs.org/api/http.html#class-httpserverresponse).
@example
```
import fs from 'node:fs';
import {isWritableStream} from 'is-stream';
isWritableStream(fs.createWriteStrem('unicorn.txt'));
//=> true
```
*/
export function isWritableStream(stream: unknown, options?: Options): stream is WritableStream;
/**
@returns Whether `stream` is a [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream_readable) or an [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage).
@example
```
import fs from 'node:fs';
import {isReadableStream} from 'is-stream';
isReadableStream(fs.createReadStream('unicorn.png'));
//=> true
```
*/
export function isReadableStream(stream: unknown, options?: Options): stream is ReadableStream;
/**
@returns Whether `stream` is a [`stream.Duplex`](https://nodejs.org/api/stream.html#stream_class_stream_duplex).
@example
```
import {Duplex as DuplexStream} from 'node:stream';
import {isDuplexStream} from 'is-stream';
isDuplexStream(new DuplexStream());
//=> true
```
*/
export function isDuplexStream(stream: unknown, options?: Options): stream is DuplexStream;
/**
@returns Whether `stream` is a [`stream.Transform`](https://nodejs.org/api/stream.html#stream_class_stream_transform).
@example
```
import fs from 'node:fs';
import StringifyStream from 'streaming-json-stringify';
import {isTransformStream} from 'is-stream';
isTransformStream(StringifyStream());
//=> true
```
*/
export function isTransformStream(stream: unknown, options?: Options): stream is TransformStream;
+37
View File
@@ -0,0 +1,37 @@
export function isStream(stream, {checkOpen = true} = {}) {
return stream !== null
&& typeof stream === 'object'
&& (stream.writable || stream.readable || !checkOpen || (stream.writable === undefined && stream.readable === undefined))
&& typeof stream.pipe === 'function';
}
export function isWritableStream(stream, {checkOpen = true} = {}) {
return isStream(stream, {checkOpen})
&& (stream.writable || !checkOpen)
&& typeof stream.write === 'function'
&& typeof stream.end === 'function'
&& typeof stream.writable === 'boolean'
&& typeof stream.writableObjectMode === 'boolean'
&& typeof stream.destroy === 'function'
&& typeof stream.destroyed === 'boolean';
}
export function isReadableStream(stream, {checkOpen = true} = {}) {
return isStream(stream, {checkOpen})
&& (stream.readable || !checkOpen)
&& typeof stream.read === 'function'
&& typeof stream.readable === 'boolean'
&& typeof stream.readableObjectMode === 'boolean'
&& typeof stream.destroy === 'function'
&& typeof stream.destroyed === 'boolean';
}
export function isDuplexStream(stream, options) {
return isWritableStream(stream, options)
&& isReadableStream(stream, options);
}
export function isTransformStream(stream, options) {
return isDuplexStream(stream, options)
&& typeof stream._transform === 'function';
}
+9
View File
@@ -0,0 +1,9 @@
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+48
View File
@@ -0,0 +1,48 @@
{
"name": "is-stream",
"version": "4.0.1",
"description": "Check if something is a Node.js stream",
"license": "MIT",
"repository": "sindresorhus/is-stream",
"funding": "https://github.com/sponsors/sindresorhus",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "https://sindresorhus.com"
},
"type": "module",
"exports": {
"types": "./index.d.ts",
"default": "./index.js"
},
"sideEffects": false,
"engines": {
"node": ">=18"
},
"scripts": {
"test": "xo && ava && tsd"
},
"files": [
"index.js",
"index.d.ts"
],
"keywords": [
"stream",
"type",
"streams",
"writable",
"readable",
"duplex",
"transform",
"check",
"detect",
"is"
],
"devDependencies": {
"@types/node": "^20.11.19",
"ava": "^5.3.1",
"tempy": "^3.1.0",
"tsd": "^0.30.5",
"xo": "^0.57.0"
}
}
+57
View File
@@ -0,0 +1,57 @@
# is-stream
> Check if something is a [Node.js stream](https://nodejs.org/api/stream.html)
## Install
```sh
npm install is-stream
```
## Usage
```js
import fs from 'node:fs';
import {isStream} from 'is-stream';
isStream(fs.createReadStream('unicorn.png'));
//=> true
isStream({});
//=> false
```
## API
### isStream(stream, options?)
Returns a `boolean` for whether it's a [`Stream`](https://nodejs.org/api/stream.html#stream_stream).
### isWritableStream(stream, options?)
Returns a `boolean` for whether it's a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable), an [`http.OutgoingMessage`](https://nodejs.org/api/http.html#class-httpoutgoingmessage), an [`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse) or an [`http.ClientRequest`](https://nodejs.org/api/http.html#class-httpserverresponse).
### isReadableStream(stream, options?)
Returns a `boolean` for whether it's a [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream_readable) or an [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage).
### isDuplexStream(stream, options?)
Returns a `boolean` for whether it's a [`stream.Duplex`](https://nodejs.org/api/stream.html#stream_class_stream_duplex).
### isTransformStream(stream, options?)
Returns a `boolean` for whether it's a [`stream.Transform`](https://nodejs.org/api/stream.html#stream_class_stream_transform).
### Options
#### checkOpen
Type: `boolean`\
Default: `true`
When this option is `true`, the method returns `false` if the stream has already been closed.
## Related
- [is-file-stream](https://github.com/jamestalmage/is-file-stream) - Detect if a stream is a file stream
+48
View File
@@ -0,0 +1,48 @@
{
"name": "compress-commons",
"version": "7.0.1",
"description": "a library that defines a common interface for working with archive formats within node",
"homepage": "https://github.com/archiverjs/node-compress-commons",
"author": {
"name": "Chris Talkington",
"url": "http://christalkington.com/"
},
"repository": {
"type": "git",
"url": "https://github.com/archiverjs/node-compress-commons.git"
},
"bugs": {
"url": "https://github.com/archiverjs/node-compress-commons/issues"
},
"license": "MIT",
"type": "module",
"exports": "./lib/compress-commons.js",
"files": [
"lib"
],
"engines": {
"node": ">=18"
},
"scripts": {
"test": "mocha --reporter dot"
},
"dependencies": {
"crc-32": "^1.2.0",
"crc32-stream": "^7.0.1",
"is-stream": "^4.0.0",
"normalize-path": "^3.0.0",
"readable-stream": "^4.0.0"
},
"devDependencies": {
"chai": "6.2.2",
"mkdirp": "3.0.1",
"mocha": "11.7.5",
"prettier": "3.8.3",
"rimraf": "6.1.3"
},
"keywords": [
"compress",
"commons",
"archive"
]
}