tar-vern 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/packer.ts","../src/utils.ts"],"sourcesContent":["// tar-vern - Tape archiver library for Typescript\n// Copyright (c) Kouji Matsui (@kekyo@mi.kekyo.net)\n// Under MIT.\n// https://github.com/kekyo/tar-vern/\n\nimport { Readable } from \"stream\";\nimport { CompressionTypes, EntryItem } from \"./types\";\nimport { createGzip } from \"zlib\";\n\n/**\n * Get the byte length of a string in UTF-8\n * @param str - The string to get the byte length of\n * @returns The byte length of the string\n */\nconst utf8ByteLength = (str: string) => {\n return Buffer.byteLength(str, \"utf8\");\n}\n\n/**\n * Truncate a string to a maximum byte length in UTF-8\n * @param str - The string to truncate\n * @param maxBytes - The maximum byte length\n * @returns The truncated string\n */\nconst truncateUtf8Safe = (str: string, maxBytes: number) => {\n let total = 0;\n let i = 0;\n while (i < str.length) {\n const codePoint = str.codePointAt(i)!;\n const char = String.fromCodePoint(codePoint);\n const charBytes = Buffer.byteLength(char, \"utf8\");\n if (total + charBytes > maxBytes) break;\n total += charBytes;\n i += char.length;\n }\n return str.slice(0, i);\n}\n\n// Tar specification: name max 100 bytes, prefix max 155 bytes\nconst MAX_NAME = 100;\nconst MAX_PREFIX = 155;\n\n/**\n * Split a path into a name and a prefix\n * @param path - The path to split\n * @returns The name and prefix\n */\nconst splitPath = (path: string) => {\n if (utf8ByteLength(path) <= MAX_NAME) {\n return { prefix: \"\", name: path };\n }\n\n // Split by '/' and find the part that fits in name from the end\n const parts = path.split(\"/\");\n let name = parts.pop() ?? \"\";\n let prefix = parts.join(\"/\");\n\n // Truncate if name exceeds 100 bytes\n if (utf8ByteLength(name) > MAX_NAME) {\n name = truncateUtf8Safe(name, MAX_NAME);\n }\n\n // Truncate if prefix exceeds 155 bytes\n while (utf8ByteLength(prefix) > MAX_PREFIX) {\n prefix = truncateUtf8Safe(prefix, MAX_PREFIX);\n }\n\n return { prefix, name };\n}\n\n///////////////////////////////////////////////////////////////////////////////////\n\n/**\n * Get octal bytes from a number\n * @param value - The number to get octal bytes from\n * @param length - The length of the octal bytes\n * @returns The octal bytes\n */\nconst getOctalBytes = (value: number, length: number) => {\n const str = value.toString(8).padStart(length - 1, \"0\") + \"\\0\";\n return Buffer.from(str, \"ascii\");\n};\n\n/**\n * Get padded bytes from a buffer\n * @param buffer - The buffer to get padded bytes from\n * @returns The padded bytes\n */\nconst getPaddedBytes = (buffer: Buffer) => {\n const extra = buffer.length % 512;\n if (extra === 0) {\n return buffer;\n } else {\n return Buffer.concat([buffer, Buffer.alloc(512 - extra, 0)]);\n }\n}\n\n/**\n * The terminator bytes\n */\nconst terminatorBytes = Buffer.alloc(1024, 0);\n\n/**\n * Create a tar header\n * @param type - The type of the entry\n * @param path - The path of the entry\n * @param size - The size of the entry\n * @param mode - The mode of the entry\n * @param uname - The user name of the entry\n * @param gname - The group name of the entry\n */\nconst createTarHeader = (\n type: 'file' | 'directory',\n path: string,\n size: number,\n mode: number,\n uname: string,\n gname: string,\n uid: number,\n gid: number,\n date: Date\n) => {\n // Allocate header bytes\n const buffer = Buffer.alloc(512, 0);\n\n // Split path into name and prefix\n const { name, prefix } = splitPath(path);\n\n // Write name, mode, uid, gid, size, mtime, typeflag, prefix, checksum\n buffer.write(name, 0, 100, \"utf8\");\n getOctalBytes(mode & 0o7777, 8).copy(buffer, 100);\n getOctalBytes(uid, 8).copy(buffer, 108);\n getOctalBytes(gid, 8).copy(buffer, 116);\n getOctalBytes(size, 12).copy(buffer, 124);\n getOctalBytes(Math.floor(date.getTime() / 1000), 12).copy(buffer, 136);\n\n // Check sum space\n Buffer.from(\" \", \"ascii\").copy(buffer, 148);\n\n if (type === 'file') {\n buffer.write(\"0\", 156, 1, \"ascii\"); // typeflag (file)\n } else {\n buffer.write(\"5\", 156, 1, \"ascii\"); // typeflag (directory)\n }\n buffer.write(\"ustar\\0\", 257, 6, \"ascii\");\n buffer.write(\"00\", 263, 2, \"ascii\"); // version\n buffer.write(uname, 265, 32, \"utf8\");\n buffer.write(gname, 297, 32, \"utf8\");\n buffer.write(prefix, 345, 155, \"utf8\"); // Path prefix\n\n // Calculate check sum\n let sum = 0;\n for (let i = 0; i < 512; i++) {\n sum += buffer[i];\n }\n getOctalBytes(sum, 8).copy(buffer, 148);\n\n return buffer;\n}\n\n///////////////////////////////////////////////////////////////////////////////////\n\n/**\n * Create a tar packer\n * @param entryItemGenerator - The async generator of the entry items\n * @param compressionType - The compression type to use (Default: 'none')\n * @param signal - The abort signal to cancel the tar packer\n * @returns Readable stream of the tar packer\n */\nexport const createTarPacker = (\n entryItemGenerator: AsyncGenerator<EntryItem, void, unknown>,\n compressionType?: CompressionTypes,\n signal?: AbortSignal) => {\n\n // Create async generator function from entry item iterator\n const entryItemIterator = async function*() {\n // Iterate over the entry items\n for await (const entryItem of entryItemGenerator) {\n signal?.throwIfAborted();\n\n switch (entryItem.kind) {\n // Entry is a file\n case 'file': {\n const entryItemContent = entryItem.content;\n // Content is a string\n if (typeof entryItemContent === 'string') {\n // Get content bytes from string\n const contentBytes = Buffer.from(entryItemContent, \"utf8\");\n\n // Create and produce tar header bytes\n const tarHeaderBytes = createTarHeader(\n 'file',\n entryItem.path,\n contentBytes.length,\n entryItem.mode,\n entryItem.uname,\n entryItem.gname,\n entryItem.uid,\n entryItem.gid,\n entryItem.date);\n yield tarHeaderBytes;\n\n // Content bytes to adjust padding space and produce\n const paddedContentBytes = getPaddedBytes(contentBytes);\n yield paddedContentBytes;\n\n // Content is a buffer\n } else if (Buffer.isBuffer(entryItemContent)) {\n // Create and produce tar header bytes\n const tarHeaderBytes = createTarHeader(\n 'file',\n entryItem.path,\n entryItemContent.length,\n entryItem.mode,\n entryItem.uname,\n entryItem.gname,\n entryItem.uid,\n entryItem.gid,\n entryItem.date);\n yield tarHeaderBytes;\n\n // Content bytes to adjust padding space and produce\n const paddedContentBytes = getPaddedBytes(entryItemContent);\n yield paddedContentBytes;\n\n } else {\n // Create and produce tar header bytes\n const tarHeaderBytes = createTarHeader(\n 'file',\n entryItem.path,\n entryItemContent.length,\n entryItem.mode,\n entryItem.uname,\n entryItem.gname,\n entryItem.uid,\n entryItem.gid,\n entryItem.date);\n yield tarHeaderBytes;\n\n switch (entryItemContent.kind) {\n // Content is a generator\n case 'generator': {\n let position = 0;\n for await (const contentFragmentBytes of entryItemContent.generator) {\n signal?.throwIfAborted();\n yield contentFragmentBytes;\n position += contentFragmentBytes.length;\n }\n\n // Padding space\n if (position % 512 !== 0) {\n signal?.throwIfAborted();\n yield Buffer.alloc(512 - (position % 512), 0);\n }\n break;\n }\n // Content is a readable stream\n case 'readable': {\n let position = 0;\n for await (const content of entryItemContent.readable) {\n signal?.throwIfAborted();\n if (typeof content === 'string') {\n const stringBytes = Buffer.from(content, \"utf8\");\n yield stringBytes;\n position += stringBytes.length;\n } else if (Buffer.isBuffer(content)) {\n yield content;\n position += content.length;\n }\n }\n\n // Padding space\n if (position % 512 !== 0) {\n signal?.throwIfAborted();\n yield Buffer.alloc(512 - (position % 512), 0);\n }\n break;\n }\n }\n }\n break;\n }\n // Entry is a directory\n case 'directory': {\n // Create and produce tar header bytes\n const tarHeaderBytes = createTarHeader(\n 'directory',\n entryItem.path,\n 0,\n entryItem.mode,\n entryItem.uname,\n entryItem.gname,\n entryItem.uid,\n entryItem.gid,\n entryItem.date\n );\n yield tarHeaderBytes;\n break;\n }\n }\n }\n\n // Terminates for tar stream\n yield terminatorBytes;\n };\n\n const ct = compressionType ?? 'none';\n\n switch (ct) {\n // No compression\n case 'none': {\n // Create readable stream from entry item iterator\n return Readable.from(entryItemIterator());\n }\n // Gzip compression\n case 'gzip': {\n // Create gzip stream\n const gzipStream = createGzip({ level: 9 });\n // Create readable stream from entry item iterator\n const entryItemStream = Readable.from(entryItemIterator());\n // Pipe the entry item stream to the gzip stream\n entryItemStream.pipe(gzipStream);\n // Return the gzip stream\n return gzipStream;\n }\n }\n};\n","// tar-vern - Tape archiver library for Typescript\n// Copyright (c) Kouji Matsui (@kekyo@mi.kekyo.net)\n// Under MIT.\n// https://github.com/kekyo/tar-vern/\n\nimport { createReadStream, createWriteStream } from \"fs\";\nimport { stat } from \"fs/promises\";\nimport { Readable } from \"stream\";\nimport { CreateItemOptions, CreateReadableItemOptions, FileItem, DirectoryItem, ReflectStats, CreateDirectoryItemOptions } from \"./types\";\n\n/**\n * Get the user/group name from the candidate name or ID\n * @param candidateName - The candidate user/group name\n * @param candidateId - The candidate user/group ID\n * @param reflectStat - Whether to reflect the stat (all, exceptName, none)\n * @returns The user/group name\n */\nconst getUName = (candidateName: string | undefined, candidateId: number, reflectStat: ReflectStats | undefined) => {\n return candidateName ?? (reflectStat === 'all' ? candidateId.toString() : 'root');\n}\n\n/**\n * Create a DirectoryItem\n * @param path - The path to the directory in the tar archive\n * @param reflectStat - Whether to reflect optional stat of the file (mode, uid, gid, mtime. Default: 'none')\n * @param options - Metadata for the directory including path in tar archive\n * @returns A DirectoryItem\n * @remarks When reflectStat is 'all' or 'exceptName', `options.directoryPath` must be provided.\n */\nexport const createDirectoryItem = async (\n path: string,\n reflectStat?: ReflectStats,\n options?: CreateDirectoryItemOptions\n): Promise<DirectoryItem> => {\n const rs = reflectStat ?? 'none';\n\n if (rs !== 'none' && options?.directoryPath) {\n const stats = await stat(options.directoryPath);\n const mode = options?.mode ?? stats.mode;\n const uid = options?.uid ?? stats.uid;\n const gid = options?.gid ?? stats.gid;\n const date = options?.date ?? stats.mtime;\n const uname = getUName(options?.uname, stats.uid, rs);\n const gname = getUName(options?.gname, stats.gid, rs);\n return {\n kind: 'directory',\n path, mode, uname, gname, uid, gid, date,\n };\n } else {\n const mode = options?.mode ?? 0o755;\n const uid = options?.uid ?? 0;\n const gid = options?.gid ?? 0;\n const date = options?.date ?? new Date();\n const uname = getUName(options?.uname, undefined, rs);\n const gname = getUName(options?.gname, undefined, rs);\n return {\n kind: 'directory',\n path, mode, uname, gname, uid, gid, date,\n };\n }\n};\n\n/**\n * Create a FileItem from a Readable stream\n * @param path - The path to the file in the tar archive\n * @param reader - The readable stream\n * @param options - Metadata for the file including path in tar archive\n * @returns A FileItem\n */\nexport const createReadableItem = async (\n path: string,\n reader: Readable,\n options?: CreateReadableItemOptions\n): Promise<FileItem> => {\n let readable = reader;\n\n // When length is not provided, calculate the total size by reading all chunks\n let length = options?.length;\n if (!length) {\n // Calculate the total size by reading all chunks\n const chunks: Buffer[] = [];\n length = 0;\n\n // Collect all chunks to calculate size\n for await (const chunk of reader) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, 'utf8');\n chunks.push(buffer);\n length += buffer.length;\n }\n\n // Create a new readable stream from the collected chunks\n readable = Readable.from(chunks);\n }\n\n const mode = options?.mode ?? 0o644;\n const uid = options?.uid ?? 0;\n const gid = options?.gid ?? 0;\n const date = options?.date ?? new Date();\n\n const uname = options?.uname ?? 'root';\n const gname = options?.gname ?? 'root';\n\n // Create a FileItem\n return {\n kind: 'file',\n path, mode, uname, gname, uid, gid, date,\n content: {\n kind: 'readable',\n length: length,\n readable: readable\n }\n };\n};\n\n/**\n * Create a FileItem from a local file path\n * @param path - The path to the file in the tar archive\n * @param filePath - The path to the file to read from real filesystem\n * @param reflectStat - Whether to reflect optional stat of the file (mode, uid, gid, mtime. Default: 'exceptName')\n * @param options - Metadata for the file including path in tar archive\n * @returns A FileItem\n */\nexport const createReadFileItem = async (\n path: string,\n filePath: string,\n reflectStat?: ReflectStats,\n options?: CreateItemOptions\n): Promise<FileItem> => {\n const rs = reflectStat ?? 'exceptName';\n\n // Get file stats to extract metadata\n const stats = await stat(filePath);\n // Create readable stream from file\n const reader = createReadStream(filePath);\n\n const mode = options?.mode ?? (rs !== 'none' ? stats.mode : undefined);\n const uid = options?.uid ?? (rs !== 'none' ? stats.uid : undefined);\n const gid = options?.gid ?? (rs !== 'none' ? stats.gid : undefined);\n const date = options?.date ?? (rs !== 'none' ? stats.mtime : undefined);\n\n const uname = getUName(options?.uname, stats.uid, rs);\n const gname = getUName(options?.gname, stats.gid, rs);\n\n // Create a FileItem\n return await createReadableItem(path, reader, {\n length: stats.size, mode, uname, gname, uid, gid, date,\n });\n};\n\n/**\n * Store a readable stream to a file\n * @param reader - The readable stream\n * @param path - The path to the file to store the readable stream to\n * @returns A promise that resolves when the stream is finished\n */\nexport const storeReaderToFile = (reader: Readable, path: string) => {\n const writer = createWriteStream(path);\n reader.pipe(writer);\n return new Promise<void>((res, rej) => {\n writer.on('finish', res);\n writer.on('error', rej);\n });\n};\n"],"names":["Readable","createGzip","stat","createReadStream","createWriteStream"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /*!
2
+ * name: tar-vern
3
+ * version: 0.1.0
4
+ * description: Tape archiver library for Typescript
5
+ * author: Kouji Matsui (@kekyo@mi.kekyo.net)
6
+ * license: MIT
7
+ * repository.url: https://github.com/kekyo/tar-vern.git
8
+ */
9
+
10
+ export type * from './types';
11
+ export * from './packer';
12
+ export * from './utils';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,mBAAmB,SAAS,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,324 @@
1
+ /*!
2
+ * name: tar-vern
3
+ * version: 0.1.0
4
+ * description: Tape archiver library for Typescript
5
+ * author: Kouji Matsui (@kekyo@mi.kekyo.net)
6
+ * license: MIT
7
+ * repository.url: https://github.com/kekyo/tar-vern.git
8
+ */
9
+ import { Readable } from "stream";
10
+ import { createGzip } from "zlib";
11
+ import { createReadStream, createWriteStream } from "fs";
12
+ import { stat } from "fs/promises";
13
+ const utf8ByteLength = (str) => {
14
+ return Buffer.byteLength(str, "utf8");
15
+ };
16
+ const truncateUtf8Safe = (str, maxBytes) => {
17
+ let total = 0;
18
+ let i = 0;
19
+ while (i < str.length) {
20
+ const codePoint = str.codePointAt(i);
21
+ const char = String.fromCodePoint(codePoint);
22
+ const charBytes = Buffer.byteLength(char, "utf8");
23
+ if (total + charBytes > maxBytes) break;
24
+ total += charBytes;
25
+ i += char.length;
26
+ }
27
+ return str.slice(0, i);
28
+ };
29
+ const MAX_NAME = 100;
30
+ const MAX_PREFIX = 155;
31
+ const splitPath = (path) => {
32
+ if (utf8ByteLength(path) <= MAX_NAME) {
33
+ return { prefix: "", name: path };
34
+ }
35
+ const parts = path.split("/");
36
+ let name = parts.pop() ?? "";
37
+ let prefix = parts.join("/");
38
+ if (utf8ByteLength(name) > MAX_NAME) {
39
+ name = truncateUtf8Safe(name, MAX_NAME);
40
+ }
41
+ while (utf8ByteLength(prefix) > MAX_PREFIX) {
42
+ prefix = truncateUtf8Safe(prefix, MAX_PREFIX);
43
+ }
44
+ return { prefix, name };
45
+ };
46
+ const getOctalBytes = (value, length) => {
47
+ const str = value.toString(8).padStart(length - 1, "0") + "\0";
48
+ return Buffer.from(str, "ascii");
49
+ };
50
+ const getPaddedBytes = (buffer) => {
51
+ const extra = buffer.length % 512;
52
+ if (extra === 0) {
53
+ return buffer;
54
+ } else {
55
+ return Buffer.concat([buffer, Buffer.alloc(512 - extra, 0)]);
56
+ }
57
+ };
58
+ const terminatorBytes = Buffer.alloc(1024, 0);
59
+ const createTarHeader = (type, path, size, mode, uname, gname, uid, gid, date) => {
60
+ const buffer = Buffer.alloc(512, 0);
61
+ const { name, prefix } = splitPath(path);
62
+ buffer.write(name, 0, 100, "utf8");
63
+ getOctalBytes(mode & 4095, 8).copy(buffer, 100);
64
+ getOctalBytes(uid, 8).copy(buffer, 108);
65
+ getOctalBytes(gid, 8).copy(buffer, 116);
66
+ getOctalBytes(size, 12).copy(buffer, 124);
67
+ getOctalBytes(Math.floor(date.getTime() / 1e3), 12).copy(buffer, 136);
68
+ Buffer.from(" ", "ascii").copy(buffer, 148);
69
+ if (type === "file") {
70
+ buffer.write("0", 156, 1, "ascii");
71
+ } else {
72
+ buffer.write("5", 156, 1, "ascii");
73
+ }
74
+ buffer.write("ustar\0", 257, 6, "ascii");
75
+ buffer.write("00", 263, 2, "ascii");
76
+ buffer.write(uname, 265, 32, "utf8");
77
+ buffer.write(gname, 297, 32, "utf8");
78
+ buffer.write(prefix, 345, 155, "utf8");
79
+ let sum = 0;
80
+ for (let i = 0; i < 512; i++) {
81
+ sum += buffer[i];
82
+ }
83
+ getOctalBytes(sum, 8).copy(buffer, 148);
84
+ return buffer;
85
+ };
86
+ const createTarPacker = (entryItemGenerator, compressionType, signal) => {
87
+ const entryItemIterator = async function* () {
88
+ for await (const entryItem of entryItemGenerator) {
89
+ signal?.throwIfAborted();
90
+ switch (entryItem.kind) {
91
+ // Entry is a file
92
+ case "file": {
93
+ const entryItemContent = entryItem.content;
94
+ if (typeof entryItemContent === "string") {
95
+ const contentBytes = Buffer.from(entryItemContent, "utf8");
96
+ const tarHeaderBytes = createTarHeader(
97
+ "file",
98
+ entryItem.path,
99
+ contentBytes.length,
100
+ entryItem.mode,
101
+ entryItem.uname,
102
+ entryItem.gname,
103
+ entryItem.uid,
104
+ entryItem.gid,
105
+ entryItem.date
106
+ );
107
+ yield tarHeaderBytes;
108
+ const paddedContentBytes = getPaddedBytes(contentBytes);
109
+ yield paddedContentBytes;
110
+ } else if (Buffer.isBuffer(entryItemContent)) {
111
+ const tarHeaderBytes = createTarHeader(
112
+ "file",
113
+ entryItem.path,
114
+ entryItemContent.length,
115
+ entryItem.mode,
116
+ entryItem.uname,
117
+ entryItem.gname,
118
+ entryItem.uid,
119
+ entryItem.gid,
120
+ entryItem.date
121
+ );
122
+ yield tarHeaderBytes;
123
+ const paddedContentBytes = getPaddedBytes(entryItemContent);
124
+ yield paddedContentBytes;
125
+ } else {
126
+ const tarHeaderBytes = createTarHeader(
127
+ "file",
128
+ entryItem.path,
129
+ entryItemContent.length,
130
+ entryItem.mode,
131
+ entryItem.uname,
132
+ entryItem.gname,
133
+ entryItem.uid,
134
+ entryItem.gid,
135
+ entryItem.date
136
+ );
137
+ yield tarHeaderBytes;
138
+ switch (entryItemContent.kind) {
139
+ // Content is a generator
140
+ case "generator": {
141
+ let position = 0;
142
+ for await (const contentFragmentBytes of entryItemContent.generator) {
143
+ signal?.throwIfAborted();
144
+ yield contentFragmentBytes;
145
+ position += contentFragmentBytes.length;
146
+ }
147
+ if (position % 512 !== 0) {
148
+ signal?.throwIfAborted();
149
+ yield Buffer.alloc(512 - position % 512, 0);
150
+ }
151
+ break;
152
+ }
153
+ // Content is a readable stream
154
+ case "readable": {
155
+ let position = 0;
156
+ for await (const content of entryItemContent.readable) {
157
+ signal?.throwIfAborted();
158
+ if (typeof content === "string") {
159
+ const stringBytes = Buffer.from(content, "utf8");
160
+ yield stringBytes;
161
+ position += stringBytes.length;
162
+ } else if (Buffer.isBuffer(content)) {
163
+ yield content;
164
+ position += content.length;
165
+ }
166
+ }
167
+ if (position % 512 !== 0) {
168
+ signal?.throwIfAborted();
169
+ yield Buffer.alloc(512 - position % 512, 0);
170
+ }
171
+ break;
172
+ }
173
+ }
174
+ }
175
+ break;
176
+ }
177
+ // Entry is a directory
178
+ case "directory": {
179
+ const tarHeaderBytes = createTarHeader(
180
+ "directory",
181
+ entryItem.path,
182
+ 0,
183
+ entryItem.mode,
184
+ entryItem.uname,
185
+ entryItem.gname,
186
+ entryItem.uid,
187
+ entryItem.gid,
188
+ entryItem.date
189
+ );
190
+ yield tarHeaderBytes;
191
+ break;
192
+ }
193
+ }
194
+ }
195
+ yield terminatorBytes;
196
+ };
197
+ const ct = compressionType ?? "none";
198
+ switch (ct) {
199
+ // No compression
200
+ case "none": {
201
+ return Readable.from(entryItemIterator());
202
+ }
203
+ // Gzip compression
204
+ case "gzip": {
205
+ const gzipStream = createGzip({ level: 9 });
206
+ const entryItemStream = Readable.from(entryItemIterator());
207
+ entryItemStream.pipe(gzipStream);
208
+ return gzipStream;
209
+ }
210
+ }
211
+ };
212
+ const getUName = (candidateName, candidateId, reflectStat) => {
213
+ return candidateName ?? (reflectStat === "all" ? candidateId.toString() : "root");
214
+ };
215
+ const createDirectoryItem = async (path, reflectStat, options) => {
216
+ const rs = reflectStat ?? "none";
217
+ if (rs !== "none" && options?.directoryPath) {
218
+ const stats = await stat(options.directoryPath);
219
+ const mode = options?.mode ?? stats.mode;
220
+ const uid = options?.uid ?? stats.uid;
221
+ const gid = options?.gid ?? stats.gid;
222
+ const date = options?.date ?? stats.mtime;
223
+ const uname = getUName(options?.uname, stats.uid, rs);
224
+ const gname = getUName(options?.gname, stats.gid, rs);
225
+ return {
226
+ kind: "directory",
227
+ path,
228
+ mode,
229
+ uname,
230
+ gname,
231
+ uid,
232
+ gid,
233
+ date
234
+ };
235
+ } else {
236
+ const mode = options?.mode ?? 493;
237
+ const uid = options?.uid ?? 0;
238
+ const gid = options?.gid ?? 0;
239
+ const date = options?.date ?? /* @__PURE__ */ new Date();
240
+ const uname = getUName(options?.uname, void 0, rs);
241
+ const gname = getUName(options?.gname, void 0, rs);
242
+ return {
243
+ kind: "directory",
244
+ path,
245
+ mode,
246
+ uname,
247
+ gname,
248
+ uid,
249
+ gid,
250
+ date
251
+ };
252
+ }
253
+ };
254
+ const createReadableItem = async (path, reader, options) => {
255
+ let readable = reader;
256
+ let length = options?.length;
257
+ if (!length) {
258
+ const chunks = [];
259
+ length = 0;
260
+ for await (const chunk of reader) {
261
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, "utf8");
262
+ chunks.push(buffer);
263
+ length += buffer.length;
264
+ }
265
+ readable = Readable.from(chunks);
266
+ }
267
+ const mode = options?.mode ?? 420;
268
+ const uid = options?.uid ?? 0;
269
+ const gid = options?.gid ?? 0;
270
+ const date = options?.date ?? /* @__PURE__ */ new Date();
271
+ const uname = options?.uname ?? "root";
272
+ const gname = options?.gname ?? "root";
273
+ return {
274
+ kind: "file",
275
+ path,
276
+ mode,
277
+ uname,
278
+ gname,
279
+ uid,
280
+ gid,
281
+ date,
282
+ content: {
283
+ kind: "readable",
284
+ length,
285
+ readable
286
+ }
287
+ };
288
+ };
289
+ const createReadFileItem = async (path, filePath, reflectStat, options) => {
290
+ const rs = reflectStat ?? "exceptName";
291
+ const stats = await stat(filePath);
292
+ const reader = createReadStream(filePath);
293
+ const mode = options?.mode ?? (rs !== "none" ? stats.mode : void 0);
294
+ const uid = options?.uid ?? (rs !== "none" ? stats.uid : void 0);
295
+ const gid = options?.gid ?? (rs !== "none" ? stats.gid : void 0);
296
+ const date = options?.date ?? (rs !== "none" ? stats.mtime : void 0);
297
+ const uname = getUName(options?.uname, stats.uid, rs);
298
+ const gname = getUName(options?.gname, stats.gid, rs);
299
+ return await createReadableItem(path, reader, {
300
+ length: stats.size,
301
+ mode,
302
+ uname,
303
+ gname,
304
+ uid,
305
+ gid,
306
+ date
307
+ });
308
+ };
309
+ const storeReaderToFile = (reader, path) => {
310
+ const writer = createWriteStream(path);
311
+ reader.pipe(writer);
312
+ return new Promise((res, rej) => {
313
+ writer.on("finish", res);
314
+ writer.on("error", rej);
315
+ });
316
+ };
317
+ export {
318
+ createDirectoryItem,
319
+ createReadFileItem,
320
+ createReadableItem,
321
+ createTarPacker,
322
+ storeReaderToFile
323
+ };
324
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/packer.ts","../src/utils.ts"],"sourcesContent":["// tar-vern - Tape archiver library for Typescript\n// Copyright (c) Kouji Matsui (@kekyo@mi.kekyo.net)\n// Under MIT.\n// https://github.com/kekyo/tar-vern/\n\nimport { Readable } from \"stream\";\nimport { CompressionTypes, EntryItem } from \"./types\";\nimport { createGzip } from \"zlib\";\n\n/**\n * Get the byte length of a string in UTF-8\n * @param str - The string to get the byte length of\n * @returns The byte length of the string\n */\nconst utf8ByteLength = (str: string) => {\n return Buffer.byteLength(str, \"utf8\");\n}\n\n/**\n * Truncate a string to a maximum byte length in UTF-8\n * @param str - The string to truncate\n * @param maxBytes - The maximum byte length\n * @returns The truncated string\n */\nconst truncateUtf8Safe = (str: string, maxBytes: number) => {\n let total = 0;\n let i = 0;\n while (i < str.length) {\n const codePoint = str.codePointAt(i)!;\n const char = String.fromCodePoint(codePoint);\n const charBytes = Buffer.byteLength(char, \"utf8\");\n if (total + charBytes > maxBytes) break;\n total += charBytes;\n i += char.length;\n }\n return str.slice(0, i);\n}\n\n// Tar specification: name max 100 bytes, prefix max 155 bytes\nconst MAX_NAME = 100;\nconst MAX_PREFIX = 155;\n\n/**\n * Split a path into a name and a prefix\n * @param path - The path to split\n * @returns The name and prefix\n */\nconst splitPath = (path: string) => {\n if (utf8ByteLength(path) <= MAX_NAME) {\n return { prefix: \"\", name: path };\n }\n\n // Split by '/' and find the part that fits in name from the end\n const parts = path.split(\"/\");\n let name = parts.pop() ?? \"\";\n let prefix = parts.join(\"/\");\n\n // Truncate if name exceeds 100 bytes\n if (utf8ByteLength(name) > MAX_NAME) {\n name = truncateUtf8Safe(name, MAX_NAME);\n }\n\n // Truncate if prefix exceeds 155 bytes\n while (utf8ByteLength(prefix) > MAX_PREFIX) {\n prefix = truncateUtf8Safe(prefix, MAX_PREFIX);\n }\n\n return { prefix, name };\n}\n\n///////////////////////////////////////////////////////////////////////////////////\n\n/**\n * Get octal bytes from a number\n * @param value - The number to get octal bytes from\n * @param length - The length of the octal bytes\n * @returns The octal bytes\n */\nconst getOctalBytes = (value: number, length: number) => {\n const str = value.toString(8).padStart(length - 1, \"0\") + \"\\0\";\n return Buffer.from(str, \"ascii\");\n};\n\n/**\n * Get padded bytes from a buffer\n * @param buffer - The buffer to get padded bytes from\n * @returns The padded bytes\n */\nconst getPaddedBytes = (buffer: Buffer) => {\n const extra = buffer.length % 512;\n if (extra === 0) {\n return buffer;\n } else {\n return Buffer.concat([buffer, Buffer.alloc(512 - extra, 0)]);\n }\n}\n\n/**\n * The terminator bytes\n */\nconst terminatorBytes = Buffer.alloc(1024, 0);\n\n/**\n * Create a tar header\n * @param type - The type of the entry\n * @param path - The path of the entry\n * @param size - The size of the entry\n * @param mode - The mode of the entry\n * @param uname - The user name of the entry\n * @param gname - The group name of the entry\n */\nconst createTarHeader = (\n type: 'file' | 'directory',\n path: string,\n size: number,\n mode: number,\n uname: string,\n gname: string,\n uid: number,\n gid: number,\n date: Date\n) => {\n // Allocate header bytes\n const buffer = Buffer.alloc(512, 0);\n\n // Split path into name and prefix\n const { name, prefix } = splitPath(path);\n\n // Write name, mode, uid, gid, size, mtime, typeflag, prefix, checksum\n buffer.write(name, 0, 100, \"utf8\");\n getOctalBytes(mode & 0o7777, 8).copy(buffer, 100);\n getOctalBytes(uid, 8).copy(buffer, 108);\n getOctalBytes(gid, 8).copy(buffer, 116);\n getOctalBytes(size, 12).copy(buffer, 124);\n getOctalBytes(Math.floor(date.getTime() / 1000), 12).copy(buffer, 136);\n\n // Check sum space\n Buffer.from(\" \", \"ascii\").copy(buffer, 148);\n\n if (type === 'file') {\n buffer.write(\"0\", 156, 1, \"ascii\"); // typeflag (file)\n } else {\n buffer.write(\"5\", 156, 1, \"ascii\"); // typeflag (directory)\n }\n buffer.write(\"ustar\\0\", 257, 6, \"ascii\");\n buffer.write(\"00\", 263, 2, \"ascii\"); // version\n buffer.write(uname, 265, 32, \"utf8\");\n buffer.write(gname, 297, 32, \"utf8\");\n buffer.write(prefix, 345, 155, \"utf8\"); // Path prefix\n\n // Calculate check sum\n let sum = 0;\n for (let i = 0; i < 512; i++) {\n sum += buffer[i];\n }\n getOctalBytes(sum, 8).copy(buffer, 148);\n\n return buffer;\n}\n\n///////////////////////////////////////////////////////////////////////////////////\n\n/**\n * Create a tar packer\n * @param entryItemGenerator - The async generator of the entry items\n * @param compressionType - The compression type to use (Default: 'none')\n * @param signal - The abort signal to cancel the tar packer\n * @returns Readable stream of the tar packer\n */\nexport const createTarPacker = (\n entryItemGenerator: AsyncGenerator<EntryItem, void, unknown>,\n compressionType?: CompressionTypes,\n signal?: AbortSignal) => {\n\n // Create async generator function from entry item iterator\n const entryItemIterator = async function*() {\n // Iterate over the entry items\n for await (const entryItem of entryItemGenerator) {\n signal?.throwIfAborted();\n\n switch (entryItem.kind) {\n // Entry is a file\n case 'file': {\n const entryItemContent = entryItem.content;\n // Content is a string\n if (typeof entryItemContent === 'string') {\n // Get content bytes from string\n const contentBytes = Buffer.from(entryItemContent, \"utf8\");\n\n // Create and produce tar header bytes\n const tarHeaderBytes = createTarHeader(\n 'file',\n entryItem.path,\n contentBytes.length,\n entryItem.mode,\n entryItem.uname,\n entryItem.gname,\n entryItem.uid,\n entryItem.gid,\n entryItem.date);\n yield tarHeaderBytes;\n\n // Content bytes to adjust padding space and produce\n const paddedContentBytes = getPaddedBytes(contentBytes);\n yield paddedContentBytes;\n\n // Content is a buffer\n } else if (Buffer.isBuffer(entryItemContent)) {\n // Create and produce tar header bytes\n const tarHeaderBytes = createTarHeader(\n 'file',\n entryItem.path,\n entryItemContent.length,\n entryItem.mode,\n entryItem.uname,\n entryItem.gname,\n entryItem.uid,\n entryItem.gid,\n entryItem.date);\n yield tarHeaderBytes;\n\n // Content bytes to adjust padding space and produce\n const paddedContentBytes = getPaddedBytes(entryItemContent);\n yield paddedContentBytes;\n\n } else {\n // Create and produce tar header bytes\n const tarHeaderBytes = createTarHeader(\n 'file',\n entryItem.path,\n entryItemContent.length,\n entryItem.mode,\n entryItem.uname,\n entryItem.gname,\n entryItem.uid,\n entryItem.gid,\n entryItem.date);\n yield tarHeaderBytes;\n\n switch (entryItemContent.kind) {\n // Content is a generator\n case 'generator': {\n let position = 0;\n for await (const contentFragmentBytes of entryItemContent.generator) {\n signal?.throwIfAborted();\n yield contentFragmentBytes;\n position += contentFragmentBytes.length;\n }\n\n // Padding space\n if (position % 512 !== 0) {\n signal?.throwIfAborted();\n yield Buffer.alloc(512 - (position % 512), 0);\n }\n break;\n }\n // Content is a readable stream\n case 'readable': {\n let position = 0;\n for await (const content of entryItemContent.readable) {\n signal?.throwIfAborted();\n if (typeof content === 'string') {\n const stringBytes = Buffer.from(content, \"utf8\");\n yield stringBytes;\n position += stringBytes.length;\n } else if (Buffer.isBuffer(content)) {\n yield content;\n position += content.length;\n }\n }\n\n // Padding space\n if (position % 512 !== 0) {\n signal?.throwIfAborted();\n yield Buffer.alloc(512 - (position % 512), 0);\n }\n break;\n }\n }\n }\n break;\n }\n // Entry is a directory\n case 'directory': {\n // Create and produce tar header bytes\n const tarHeaderBytes = createTarHeader(\n 'directory',\n entryItem.path,\n 0,\n entryItem.mode,\n entryItem.uname,\n entryItem.gname,\n entryItem.uid,\n entryItem.gid,\n entryItem.date\n );\n yield tarHeaderBytes;\n break;\n }\n }\n }\n\n // Terminates for tar stream\n yield terminatorBytes;\n };\n\n const ct = compressionType ?? 'none';\n\n switch (ct) {\n // No compression\n case 'none': {\n // Create readable stream from entry item iterator\n return Readable.from(entryItemIterator());\n }\n // Gzip compression\n case 'gzip': {\n // Create gzip stream\n const gzipStream = createGzip({ level: 9 });\n // Create readable stream from entry item iterator\n const entryItemStream = Readable.from(entryItemIterator());\n // Pipe the entry item stream to the gzip stream\n entryItemStream.pipe(gzipStream);\n // Return the gzip stream\n return gzipStream;\n }\n }\n};\n","// tar-vern - Tape archiver library for Typescript\n// Copyright (c) Kouji Matsui (@kekyo@mi.kekyo.net)\n// Under MIT.\n// https://github.com/kekyo/tar-vern/\n\nimport { createReadStream, createWriteStream } from \"fs\";\nimport { stat } from \"fs/promises\";\nimport { Readable } from \"stream\";\nimport { CreateItemOptions, CreateReadableItemOptions, FileItem, DirectoryItem, ReflectStats, CreateDirectoryItemOptions } from \"./types\";\n\n/**\n * Get the user/group name from the candidate name or ID\n * @param candidateName - The candidate user/group name\n * @param candidateId - The candidate user/group ID\n * @param reflectStat - Whether to reflect the stat (all, exceptName, none)\n * @returns The user/group name\n */\nconst getUName = (candidateName: string | undefined, candidateId: number, reflectStat: ReflectStats | undefined) => {\n return candidateName ?? (reflectStat === 'all' ? candidateId.toString() : 'root');\n}\n\n/**\n * Create a DirectoryItem\n * @param path - The path to the directory in the tar archive\n * @param reflectStat - Whether to reflect optional stat of the file (mode, uid, gid, mtime. Default: 'none')\n * @param options - Metadata for the directory including path in tar archive\n * @returns A DirectoryItem\n * @remarks When reflectStat is 'all' or 'exceptName', `options.directoryPath` must be provided.\n */\nexport const createDirectoryItem = async (\n path: string,\n reflectStat?: ReflectStats,\n options?: CreateDirectoryItemOptions\n): Promise<DirectoryItem> => {\n const rs = reflectStat ?? 'none';\n\n if (rs !== 'none' && options?.directoryPath) {\n const stats = await stat(options.directoryPath);\n const mode = options?.mode ?? stats.mode;\n const uid = options?.uid ?? stats.uid;\n const gid = options?.gid ?? stats.gid;\n const date = options?.date ?? stats.mtime;\n const uname = getUName(options?.uname, stats.uid, rs);\n const gname = getUName(options?.gname, stats.gid, rs);\n return {\n kind: 'directory',\n path, mode, uname, gname, uid, gid, date,\n };\n } else {\n const mode = options?.mode ?? 0o755;\n const uid = options?.uid ?? 0;\n const gid = options?.gid ?? 0;\n const date = options?.date ?? new Date();\n const uname = getUName(options?.uname, undefined, rs);\n const gname = getUName(options?.gname, undefined, rs);\n return {\n kind: 'directory',\n path, mode, uname, gname, uid, gid, date,\n };\n }\n};\n\n/**\n * Create a FileItem from a Readable stream\n * @param path - The path to the file in the tar archive\n * @param reader - The readable stream\n * @param options - Metadata for the file including path in tar archive\n * @returns A FileItem\n */\nexport const createReadableItem = async (\n path: string,\n reader: Readable,\n options?: CreateReadableItemOptions\n): Promise<FileItem> => {\n let readable = reader;\n\n // When length is not provided, calculate the total size by reading all chunks\n let length = options?.length;\n if (!length) {\n // Calculate the total size by reading all chunks\n const chunks: Buffer[] = [];\n length = 0;\n\n // Collect all chunks to calculate size\n for await (const chunk of reader) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, 'utf8');\n chunks.push(buffer);\n length += buffer.length;\n }\n\n // Create a new readable stream from the collected chunks\n readable = Readable.from(chunks);\n }\n\n const mode = options?.mode ?? 0o644;\n const uid = options?.uid ?? 0;\n const gid = options?.gid ?? 0;\n const date = options?.date ?? new Date();\n\n const uname = options?.uname ?? 'root';\n const gname = options?.gname ?? 'root';\n\n // Create a FileItem\n return {\n kind: 'file',\n path, mode, uname, gname, uid, gid, date,\n content: {\n kind: 'readable',\n length: length,\n readable: readable\n }\n };\n};\n\n/**\n * Create a FileItem from a local file path\n * @param path - The path to the file in the tar archive\n * @param filePath - The path to the file to read from real filesystem\n * @param reflectStat - Whether to reflect optional stat of the file (mode, uid, gid, mtime. Default: 'exceptName')\n * @param options - Metadata for the file including path in tar archive\n * @returns A FileItem\n */\nexport const createReadFileItem = async (\n path: string,\n filePath: string,\n reflectStat?: ReflectStats,\n options?: CreateItemOptions\n): Promise<FileItem> => {\n const rs = reflectStat ?? 'exceptName';\n\n // Get file stats to extract metadata\n const stats = await stat(filePath);\n // Create readable stream from file\n const reader = createReadStream(filePath);\n\n const mode = options?.mode ?? (rs !== 'none' ? stats.mode : undefined);\n const uid = options?.uid ?? (rs !== 'none' ? stats.uid : undefined);\n const gid = options?.gid ?? (rs !== 'none' ? stats.gid : undefined);\n const date = options?.date ?? (rs !== 'none' ? stats.mtime : undefined);\n\n const uname = getUName(options?.uname, stats.uid, rs);\n const gname = getUName(options?.gname, stats.gid, rs);\n\n // Create a FileItem\n return await createReadableItem(path, reader, {\n length: stats.size, mode, uname, gname, uid, gid, date,\n });\n};\n\n/**\n * Store a readable stream to a file\n * @param reader - The readable stream\n * @param path - The path to the file to store the readable stream to\n * @returns A promise that resolves when the stream is finished\n */\nexport const storeReaderToFile = (reader: Readable, path: string) => {\n const writer = createWriteStream(path);\n reader.pipe(writer);\n return new Promise<void>((res, rej) => {\n writer.on('finish', res);\n writer.on('error', rej);\n });\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /*!
2
+ * name: tar-vern
3
+ * version: 0.1.0
4
+ * description: Tape archiver library for Typescript
5
+ * author: Kouji Matsui (@kekyo@mi.kekyo.net)
6
+ * license: MIT
7
+ * repository.url: https://github.com/kekyo/tar-vern.git
8
+ */
9
+
10
+ import { Readable } from 'stream';
11
+ import { CompressionTypes, EntryItem } from './types';
12
+ /**
13
+ * Create a tar packer
14
+ * @param entryItemGenerator - The async generator of the entry items
15
+ * @param compressionType - The compression type to use (Default: 'none')
16
+ * @param signal - The abort signal to cancel the tar packer
17
+ * @returns Readable stream of the tar packer
18
+ */
19
+ export declare const createTarPacker: (entryItemGenerator: AsyncGenerator<EntryItem, void, unknown>, compressionType?: CompressionTypes, signal?: AbortSignal) => Readable;
20
+ //# sourceMappingURL=packer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packer.d.ts","sourceRoot":"","sources":["../src/packer.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AA4JtD;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAC1B,oBAAoB,cAAc,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,EAC5D,kBAAkB,gBAAgB,EAClC,SAAS,WAAW,aA0JrB,CAAC"}
@@ -0,0 +1,159 @@
1
+ /*!
2
+ * name: tar-vern
3
+ * version: 0.1.0
4
+ * description: Tape archiver library for Typescript
5
+ * author: Kouji Matsui (@kekyo@mi.kekyo.net)
6
+ * license: MIT
7
+ * repository.url: https://github.com/kekyo/tar-vern.git
8
+ */
9
+
10
+ import { Readable } from 'stream';
11
+ /**
12
+ * Base interface for all entry item contents
13
+ */
14
+ export interface EntryItemContentBase {
15
+ readonly kind: 'generator' | 'readable';
16
+ /**
17
+ * The length of the item
18
+ */
19
+ readonly length: number;
20
+ }
21
+ /**
22
+ * Interface for all entry item contents that are generators
23
+ */
24
+ export interface EntryItemGeneratorContent extends EntryItemContentBase {
25
+ readonly kind: 'generator';
26
+ /**
27
+ * The generator function
28
+ */
29
+ readonly generator: AsyncGenerator<Buffer, void, unknown>;
30
+ }
31
+ /**
32
+ * Interface for all entry item contents that are readable streams
33
+ */
34
+ export interface EntryItemReadableContent extends EntryItemContentBase {
35
+ readonly kind: 'readable';
36
+ /**
37
+ * The readable stream
38
+ */
39
+ readonly readable: Readable;
40
+ }
41
+ /**
42
+ * Union type for all entry item contents
43
+ */
44
+ export type EntryItemContent = EntryItemGeneratorContent | EntryItemReadableContent;
45
+ /**
46
+ * Base interface for all entry items
47
+ */
48
+ export interface EntryItemBase {
49
+ readonly kind: 'file' | 'directory';
50
+ /**
51
+ * The path of the item
52
+ */
53
+ readonly path: string;
54
+ /**
55
+ * The mode of the item
56
+ */
57
+ readonly mode: number;
58
+ /**
59
+ * The user name of the item
60
+ */
61
+ readonly uname: string;
62
+ /**
63
+ * The group name of the item
64
+ */
65
+ readonly gname: string;
66
+ /**
67
+ * The user ID of the item
68
+ */
69
+ readonly uid: number;
70
+ /**
71
+ * The group ID of the item
72
+ */
73
+ readonly gid: number;
74
+ /**
75
+ * The date of the item
76
+ */
77
+ readonly date: Date;
78
+ }
79
+ /**
80
+ * Interface for all file entry items
81
+ */
82
+ export interface FileItem extends EntryItemBase {
83
+ readonly kind: 'file';
84
+ /**
85
+ * The content of the item
86
+ */
87
+ readonly content: string | Buffer | EntryItemContent;
88
+ }
89
+ /**
90
+ * Interface for all directory entry items
91
+ */
92
+ export interface DirectoryItem extends EntryItemBase {
93
+ readonly kind: 'directory';
94
+ }
95
+ /**
96
+ * Union type for all entry items
97
+ */
98
+ export type EntryItem = FileItem | DirectoryItem;
99
+ /**
100
+ * The type of stat reflection
101
+ * - all: Reflect all stats
102
+ * - exceptName: Reflect all stats except uname and gname
103
+ * - none: Do not reflect any stats
104
+ */
105
+ export type ReflectStats = 'all' | 'exceptName' | 'none';
106
+ /**
107
+ * The type of compression
108
+ * - none: No compression
109
+ * - gzip: Gzip compression
110
+ */
111
+ export type CompressionTypes = 'none' | 'gzip';
112
+ /**
113
+ * Options for creating an item
114
+ */
115
+ export interface CreateItemOptions {
116
+ /**
117
+ * The mode of the item
118
+ */
119
+ readonly mode?: number;
120
+ /**
121
+ * The user name of the item
122
+ */
123
+ readonly uname?: string;
124
+ /**
125
+ * The group name of the item
126
+ */
127
+ readonly gname?: string;
128
+ /**
129
+ * The user ID of the item
130
+ */
131
+ readonly uid?: number;
132
+ /**
133
+ * The group ID of the item
134
+ */
135
+ readonly gid?: number;
136
+ /**
137
+ * The date of the item
138
+ */
139
+ readonly date?: Date;
140
+ }
141
+ /**
142
+ * Options for creating a directory item
143
+ */
144
+ export interface CreateDirectoryItemOptions extends CreateItemOptions {
145
+ /**
146
+ * The real directory path
147
+ */
148
+ readonly directoryPath?: string;
149
+ }
150
+ /**
151
+ * Options for creating a readable item
152
+ */
153
+ export interface CreateReadableItemOptions extends CreateItemOptions {
154
+ /**
155
+ * The length of the item
156
+ */
157
+ readonly length?: number;
158
+ }
159
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC;IACxC;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,yBAA0B,SAAQ,oBAAoB;IACrE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;CAC3D;AAED;;GAEG;AACH,MAAM,WAAW,wBAAyB,SAAQ,oBAAoB;IACpE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,yBAAyB,GAAG,wBAAwB,CAAC;AAIpF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IACpC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,QAAS,SAAQ,aAAa;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,gBAAgB,CAAC;CACtD;AAED;;GAEG;AACH,MAAM,WAAW,aAAc,SAAQ,aAAa;IAClD,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,aAAa,CAAC;AAIjD;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,YAAY,GAAG,MAAM,CAAC;AAEzD;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,0BAA2B,SAAQ,iBAAiB;IACnE;;OAEG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,yBAA0B,SAAQ,iBAAiB;IAClE;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B"}