@enconvo/dxt 0.2.6
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.
- package/LICENSE.md +7 -0
- package/README.md +118 -0
- package/dist/browser.d.ts +3 -0
- package/dist/browser.js +4 -0
- package/dist/cli/cli.d.ts +2 -0
- package/dist/cli/cli.js +275 -0
- package/dist/cli/init.d.ts +185 -0
- package/dist/cli/init.js +704 -0
- package/dist/cli/pack.d.ts +7 -0
- package/dist/cli/pack.js +194 -0
- package/dist/cli/unpack.d.ts +7 -0
- package/dist/cli/unpack.js +101 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +11 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +10 -0
- package/dist/node/files.d.ts +20 -0
- package/dist/node/files.js +115 -0
- package/dist/node/sign.d.ts +32 -0
- package/dist/node/sign.js +333 -0
- package/dist/node/validate.d.ts +2 -0
- package/dist/node/validate.js +124 -0
- package/dist/node.d.ts +6 -0
- package/dist/node.js +8 -0
- package/dist/schemas-loose.d.ts +629 -0
- package/dist/schemas-loose.js +97 -0
- package/dist/schemas.d.ts +637 -0
- package/dist/schemas.js +98 -0
- package/dist/shared/config.d.ts +34 -0
- package/dist/shared/config.js +157 -0
- package/dist/shared/log.d.ts +11 -0
- package/dist/shared/log.js +29 -0
- package/dist/types.d.ts +23 -0
- package/dist/types.js +1 -0
- package/package.json +83 -0
package/dist/cli/pack.js
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
import { confirm } from "@inquirer/prompts";
|
2
|
+
import { createHash } from "crypto";
|
3
|
+
import { zipSync } from "fflate";
|
4
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync, } from "fs";
|
5
|
+
import { basename, join, relative, resolve, sep } from "path";
|
6
|
+
import { getAllFilesWithCount, readDxtIgnorePatterns } from "../node/files.js";
|
7
|
+
import { validateManifest } from "../node/validate.js";
|
8
|
+
import { DxtManifestSchema } from "../schemas.js";
|
9
|
+
import { getLogger } from "../shared/log.js";
|
10
|
+
import { initExtension } from "./init.js";
|
11
|
+
function formatFileSize(bytes) {
|
12
|
+
if (bytes < 1024) {
|
13
|
+
return `${bytes}B`;
|
14
|
+
}
|
15
|
+
else if (bytes < 1024 * 1024) {
|
16
|
+
return `${(bytes / 1024).toFixed(1)}kB`;
|
17
|
+
}
|
18
|
+
else {
|
19
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
20
|
+
}
|
21
|
+
}
|
22
|
+
function sanitizeNameForFilename(name) {
|
23
|
+
// Replace spaces with hyphens
|
24
|
+
// Remove or replace characters that are problematic in filenames
|
25
|
+
return name
|
26
|
+
.toLowerCase()
|
27
|
+
.replace(/\s+/g, "-") // Replace spaces with hyphens
|
28
|
+
.replace(/[^a-z0-9-_.]/g, "") // Keep only alphanumeric, hyphens, underscores, and dots
|
29
|
+
.replace(/-+/g, "-") // Replace multiple hyphens with single hyphen
|
30
|
+
.replace(/^-+|-+$/g, "") // Remove leading/trailing hyphens
|
31
|
+
.substring(0, 100); // Limit length to 100 characters
|
32
|
+
}
|
33
|
+
export async function packExtension({ extensionPath, outputPath, silent, }) {
|
34
|
+
const resolvedPath = resolve(extensionPath);
|
35
|
+
const logger = getLogger({ silent });
|
36
|
+
// Check if directory exists
|
37
|
+
if (!existsSync(resolvedPath) || !statSync(resolvedPath).isDirectory()) {
|
38
|
+
logger.error(`ERROR: Directory not found: ${extensionPath}`);
|
39
|
+
return false;
|
40
|
+
}
|
41
|
+
// Check if manifest exists
|
42
|
+
const manifestPath = join(resolvedPath, "manifest.json");
|
43
|
+
if (!existsSync(manifestPath)) {
|
44
|
+
logger.log(`No manifest.json found in ${extensionPath}`);
|
45
|
+
const shouldInit = await confirm({
|
46
|
+
message: "Would you like to create a manifest.json file?",
|
47
|
+
default: true,
|
48
|
+
});
|
49
|
+
if (shouldInit) {
|
50
|
+
const success = await initExtension(extensionPath);
|
51
|
+
if (!success) {
|
52
|
+
logger.error("ERROR: Failed to create manifest");
|
53
|
+
return false;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
else {
|
57
|
+
logger.error("ERROR: Cannot pack extension without manifest.json");
|
58
|
+
return false;
|
59
|
+
}
|
60
|
+
}
|
61
|
+
// Validate manifest first
|
62
|
+
logger.log("Validating manifest...");
|
63
|
+
if (!validateManifest(manifestPath)) {
|
64
|
+
logger.error("ERROR: Cannot pack extension with invalid manifest");
|
65
|
+
return false;
|
66
|
+
}
|
67
|
+
// Read and parse manifest
|
68
|
+
let manifest;
|
69
|
+
try {
|
70
|
+
const manifestContent = readFileSync(manifestPath, "utf-8");
|
71
|
+
const manifestData = JSON.parse(manifestContent);
|
72
|
+
manifest = DxtManifestSchema.parse(manifestData);
|
73
|
+
}
|
74
|
+
catch (error) {
|
75
|
+
logger.error("ERROR: Failed to parse manifest.json");
|
76
|
+
if (error instanceof Error) {
|
77
|
+
logger.error(` ${error.message}`);
|
78
|
+
}
|
79
|
+
return false;
|
80
|
+
}
|
81
|
+
// Determine output path
|
82
|
+
const extensionName = basename(resolvedPath);
|
83
|
+
const finalOutputPath = outputPath
|
84
|
+
? resolve(outputPath)
|
85
|
+
: resolve(`${extensionName}.dxt`);
|
86
|
+
// Ensure output directory exists
|
87
|
+
const outputDir = join(finalOutputPath, "..");
|
88
|
+
mkdirSync(outputDir, { recursive: true });
|
89
|
+
try {
|
90
|
+
// Read .dxtignore patterns if present
|
91
|
+
const dxtIgnorePatterns = readDxtIgnorePatterns(resolvedPath);
|
92
|
+
// Get all files in the extension directory
|
93
|
+
const { files, ignoredCount } = getAllFilesWithCount(resolvedPath, resolvedPath, {}, dxtIgnorePatterns);
|
94
|
+
// Print package header
|
95
|
+
logger.log(`\n📦 ${manifest.name}@${manifest.version}`);
|
96
|
+
// Print file list
|
97
|
+
logger.log("Archive Contents");
|
98
|
+
const fileEntries = Object.entries(files);
|
99
|
+
let totalUnpackedSize = 0;
|
100
|
+
// Sort files for consistent output
|
101
|
+
fileEntries.sort(([a], [b]) => a.localeCompare(b));
|
102
|
+
// Group files by directory for deep nesting
|
103
|
+
const directoryGroups = new Map();
|
104
|
+
const shallowFiles = [];
|
105
|
+
for (const [filePath, fileData] of fileEntries) {
|
106
|
+
const relPath = relative(resolvedPath, filePath);
|
107
|
+
const content = fileData.data;
|
108
|
+
const size = typeof content === "string"
|
109
|
+
? Buffer.byteLength(content, "utf8")
|
110
|
+
: content.length;
|
111
|
+
totalUnpackedSize += size;
|
112
|
+
// Check if file is deeply nested (3+ levels)
|
113
|
+
const parts = relPath.split(sep);
|
114
|
+
if (parts.length > 3) {
|
115
|
+
// Group by the first 3 directory levels
|
116
|
+
const groupKey = parts.slice(0, 3).join("/");
|
117
|
+
if (!directoryGroups.has(groupKey)) {
|
118
|
+
directoryGroups.set(groupKey, { files: [], totalSize: 0 });
|
119
|
+
}
|
120
|
+
const group = directoryGroups.get(groupKey);
|
121
|
+
group.files.push(relPath);
|
122
|
+
group.totalSize += size;
|
123
|
+
}
|
124
|
+
else {
|
125
|
+
shallowFiles.push({ path: relPath, size });
|
126
|
+
}
|
127
|
+
}
|
128
|
+
// Print shallow files first
|
129
|
+
for (const { path, size } of shallowFiles) {
|
130
|
+
logger.log(`${formatFileSize(size).padStart(8)} ${path}`);
|
131
|
+
}
|
132
|
+
// Print grouped directories
|
133
|
+
for (const [dir, { files, totalSize }] of directoryGroups) {
|
134
|
+
if (files.length === 1) {
|
135
|
+
// If only one file in the group, print it normally
|
136
|
+
const filePath = files[0];
|
137
|
+
const fileSize = totalSize;
|
138
|
+
logger.log(`${formatFileSize(fileSize).padStart(8)} ${filePath}`);
|
139
|
+
}
|
140
|
+
else {
|
141
|
+
// Print directory summary
|
142
|
+
logger.log(`${formatFileSize(totalSize).padStart(8)} ${dir}/ [and ${files.length} more files]`);
|
143
|
+
}
|
144
|
+
}
|
145
|
+
// Create zip with preserved file permissions
|
146
|
+
const zipFiles = {};
|
147
|
+
const isUnix = process.platform !== "win32";
|
148
|
+
for (const [filePath, fileData] of Object.entries(files)) {
|
149
|
+
if (isUnix) {
|
150
|
+
// Set external file attributes to preserve Unix permissions
|
151
|
+
// The mode needs to be shifted to the upper 16 bits for ZIP format
|
152
|
+
zipFiles[filePath] = [
|
153
|
+
fileData.data,
|
154
|
+
{ os: 3, attrs: (fileData.mode & 0o777) << 16 },
|
155
|
+
];
|
156
|
+
}
|
157
|
+
else {
|
158
|
+
// On Windows, use default ZIP attributes (no Unix permissions)
|
159
|
+
zipFiles[filePath] = fileData.data;
|
160
|
+
}
|
161
|
+
}
|
162
|
+
const zipData = zipSync(zipFiles, {
|
163
|
+
level: 9, // Maximum compression
|
164
|
+
mtime: new Date(),
|
165
|
+
});
|
166
|
+
// Write zip file
|
167
|
+
writeFileSync(finalOutputPath, zipData);
|
168
|
+
// Calculate SHA sum
|
169
|
+
const shasum = createHash("sha1").update(zipData).digest("hex");
|
170
|
+
// Print archive details
|
171
|
+
const sanitizedName = sanitizeNameForFilename(manifest.name);
|
172
|
+
const archiveName = `${sanitizedName}-${manifest.version}.dxt`;
|
173
|
+
logger.log("\nArchive Details");
|
174
|
+
logger.log(`name: ${manifest.name}`);
|
175
|
+
logger.log(`version: ${manifest.version}`);
|
176
|
+
logger.log(`filename: ${archiveName}`);
|
177
|
+
logger.log(`package size: ${formatFileSize(zipData.length)}`);
|
178
|
+
logger.log(`unpacked size: ${formatFileSize(totalUnpackedSize)}`);
|
179
|
+
logger.log(`shasum: ${shasum}`);
|
180
|
+
logger.log(`total files: ${fileEntries.length}`);
|
181
|
+
logger.log(`ignored (.dxtignore) files: ${ignoredCount}`);
|
182
|
+
logger.log(`\nOutput: ${finalOutputPath}`);
|
183
|
+
return true;
|
184
|
+
}
|
185
|
+
catch (error) {
|
186
|
+
if (error instanceof Error) {
|
187
|
+
logger.error(`ERROR: Archive error: ${error.message}`);
|
188
|
+
}
|
189
|
+
else {
|
190
|
+
logger.error("ERROR: Unknown archive error occurred");
|
191
|
+
}
|
192
|
+
return false;
|
193
|
+
}
|
194
|
+
}
|
@@ -0,0 +1,101 @@
|
|
1
|
+
import { unzipSync } from "fflate";
|
2
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync, } from "fs";
|
3
|
+
import { join, resolve, sep } from "path";
|
4
|
+
import { extractSignatureBlock } from "../node/sign.js";
|
5
|
+
import { getLogger } from "../shared/log.js";
|
6
|
+
export async function unpackExtension({ dxtPath, outputDir, silent, }) {
|
7
|
+
const logger = getLogger({ silent });
|
8
|
+
const resolvedDxtPath = resolve(dxtPath);
|
9
|
+
if (!existsSync(resolvedDxtPath)) {
|
10
|
+
logger.error(`ERROR: DXT file not found: ${dxtPath}`);
|
11
|
+
return false;
|
12
|
+
}
|
13
|
+
const finalOutputDir = outputDir ? resolve(outputDir) : process.cwd();
|
14
|
+
if (!existsSync(finalOutputDir)) {
|
15
|
+
mkdirSync(finalOutputDir, { recursive: true });
|
16
|
+
}
|
17
|
+
try {
|
18
|
+
const fileContent = readFileSync(resolvedDxtPath);
|
19
|
+
const { originalContent } = extractSignatureBlock(fileContent);
|
20
|
+
// Parse file attributes from ZIP central directory
|
21
|
+
const fileAttributes = new Map();
|
22
|
+
const isUnix = process.platform !== "win32";
|
23
|
+
if (isUnix) {
|
24
|
+
// Parse ZIP central directory to extract file attributes
|
25
|
+
const zipBuffer = originalContent;
|
26
|
+
// Find end of central directory record
|
27
|
+
let eocdOffset = -1;
|
28
|
+
for (let i = zipBuffer.length - 22; i >= 0; i--) {
|
29
|
+
if (zipBuffer.readUInt32LE(i) === 0x06054b50) {
|
30
|
+
eocdOffset = i;
|
31
|
+
break;
|
32
|
+
}
|
33
|
+
}
|
34
|
+
if (eocdOffset !== -1) {
|
35
|
+
const centralDirOffset = zipBuffer.readUInt32LE(eocdOffset + 16);
|
36
|
+
const centralDirEntries = zipBuffer.readUInt16LE(eocdOffset + 8);
|
37
|
+
let offset = centralDirOffset;
|
38
|
+
for (let i = 0; i < centralDirEntries; i++) {
|
39
|
+
if (zipBuffer.readUInt32LE(offset) === 0x02014b50) {
|
40
|
+
const externalAttrs = zipBuffer.readUInt32LE(offset + 38);
|
41
|
+
const filenameLength = zipBuffer.readUInt16LE(offset + 28);
|
42
|
+
const filename = zipBuffer.toString("utf8", offset + 46, offset + 46 + filenameLength);
|
43
|
+
// Extract Unix permissions from external attributes (upper 16 bits)
|
44
|
+
const mode = (externalAttrs >> 16) & 0o777;
|
45
|
+
if (mode > 0) {
|
46
|
+
fileAttributes.set(filename, mode);
|
47
|
+
}
|
48
|
+
const extraFieldLength = zipBuffer.readUInt16LE(offset + 30);
|
49
|
+
const commentLength = zipBuffer.readUInt16LE(offset + 32);
|
50
|
+
offset += 46 + filenameLength + extraFieldLength + commentLength;
|
51
|
+
}
|
52
|
+
else {
|
53
|
+
break;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
}
|
58
|
+
const decompressed = unzipSync(originalContent);
|
59
|
+
for (const relativePath in decompressed) {
|
60
|
+
if (Object.prototype.hasOwnProperty.call(decompressed, relativePath)) {
|
61
|
+
const data = decompressed[relativePath];
|
62
|
+
const fullPath = join(finalOutputDir, relativePath);
|
63
|
+
// Prevent zip slip attacks by validating the resolved path
|
64
|
+
const normalizedPath = resolve(fullPath);
|
65
|
+
const normalizedOutputDir = resolve(finalOutputDir);
|
66
|
+
if (!normalizedPath.startsWith(normalizedOutputDir + sep) &&
|
67
|
+
normalizedPath !== normalizedOutputDir) {
|
68
|
+
throw new Error(`Path traversal attempt detected: ${relativePath}`);
|
69
|
+
}
|
70
|
+
const dir = join(fullPath, "..");
|
71
|
+
if (!existsSync(dir)) {
|
72
|
+
mkdirSync(dir, { recursive: true });
|
73
|
+
}
|
74
|
+
writeFileSync(fullPath, data);
|
75
|
+
// Restore Unix file permissions if available
|
76
|
+
if (isUnix && fileAttributes.has(relativePath)) {
|
77
|
+
try {
|
78
|
+
const mode = fileAttributes.get(relativePath);
|
79
|
+
if (mode !== undefined) {
|
80
|
+
chmodSync(fullPath, mode);
|
81
|
+
}
|
82
|
+
}
|
83
|
+
catch (error) {
|
84
|
+
// Silently ignore permission errors
|
85
|
+
}
|
86
|
+
}
|
87
|
+
}
|
88
|
+
}
|
89
|
+
logger.log(`Extension unpacked successfully to ${finalOutputDir}`);
|
90
|
+
return true;
|
91
|
+
}
|
92
|
+
catch (error) {
|
93
|
+
if (error instanceof Error) {
|
94
|
+
logger.error(`ERROR: Failed to unpack extension: ${error.message}`);
|
95
|
+
}
|
96
|
+
else {
|
97
|
+
logger.error("ERROR: An unknown error occurred during unpacking.");
|
98
|
+
}
|
99
|
+
return false;
|
100
|
+
}
|
101
|
+
}
|
package/dist/cli.d.ts
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
export * from "./cli/init.js";
|
2
|
+
export * from "./cli/pack.js";
|
3
|
+
export * from "./schemas.js";
|
4
|
+
export * from "./shared/config.js";
|
5
|
+
export * from "./types.js";
|
6
|
+
export * from "./node/files.js";
|
7
|
+
export * from "./node/sign.js";
|
8
|
+
export * from "./node/validate.js";
|
package/dist/cli.js
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
// CLI-specific exports
|
2
|
+
export * from "./cli/init.js";
|
3
|
+
export * from "./cli/pack.js";
|
4
|
+
// Include all shared exports
|
5
|
+
export * from "./schemas.js";
|
6
|
+
export * from "./shared/config.js";
|
7
|
+
export * from "./types.js";
|
8
|
+
// Include node exports since CLI needs them
|
9
|
+
export * from "./node/files.js";
|
10
|
+
export * from "./node/sign.js";
|
11
|
+
export * from "./node/validate.js";
|
package/dist/index.d.ts
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
export * from "./cli/init.js";
|
2
|
+
export * from "./cli/pack.js";
|
3
|
+
export * from "./cli/unpack.js";
|
4
|
+
export * from "./node/files.js";
|
5
|
+
export * from "./node/sign.js";
|
6
|
+
export * from "./node/validate.js";
|
7
|
+
export * from "./schemas.js";
|
8
|
+
export * from "./shared/config.js";
|
9
|
+
export * from "./types.js";
|
package/dist/index.js
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
// Default export includes everything (backward compatibility)
|
2
|
+
export * from "./cli/init.js";
|
3
|
+
export * from "./cli/pack.js";
|
4
|
+
export * from "./cli/unpack.js";
|
5
|
+
export * from "./node/files.js";
|
6
|
+
export * from "./node/sign.js";
|
7
|
+
export * from "./node/validate.js";
|
8
|
+
export * from "./schemas.js";
|
9
|
+
export * from "./shared/config.js";
|
10
|
+
export * from "./types.js";
|
@@ -0,0 +1,20 @@
|
|
1
|
+
export declare const EXCLUDE_PATTERNS: string[];
|
2
|
+
/**
|
3
|
+
* Read and parse .dxtignore file patterns
|
4
|
+
*/
|
5
|
+
export declare function readDxtIgnorePatterns(baseDir: string): string[];
|
6
|
+
/**
|
7
|
+
* Used for testing, calls the same methods as the other ignore checks
|
8
|
+
*/
|
9
|
+
export declare function shouldExclude(filePath: string, additionalPatterns?: string[]): boolean;
|
10
|
+
export declare function getAllFiles(dirPath: string, baseDir?: string, fileList?: Record<string, Uint8Array>, additionalPatterns?: string[]): Record<string, Uint8Array>;
|
11
|
+
interface FileWithPermissions {
|
12
|
+
data: Uint8Array;
|
13
|
+
mode: number;
|
14
|
+
}
|
15
|
+
export interface GetAllFilesResult {
|
16
|
+
files: Record<string, FileWithPermissions>;
|
17
|
+
ignoredCount: number;
|
18
|
+
}
|
19
|
+
export declare function getAllFilesWithCount(dirPath: string, baseDir?: string, fileList?: Record<string, FileWithPermissions>, additionalPatterns?: string[], ignoredCount?: number): GetAllFilesResult;
|
20
|
+
export {};
|
@@ -0,0 +1,115 @@
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
|
2
|
+
import ignore from "ignore";
|
3
|
+
import { join, relative, sep } from "path";
|
4
|
+
// Files/patterns to exclude from the package
|
5
|
+
export const EXCLUDE_PATTERNS = [
|
6
|
+
".DS_Store",
|
7
|
+
"Thumbs.db",
|
8
|
+
".gitignore",
|
9
|
+
".git",
|
10
|
+
".dxtignore",
|
11
|
+
"*.log",
|
12
|
+
".env*",
|
13
|
+
".npm",
|
14
|
+
".npmrc",
|
15
|
+
".yarnrc",
|
16
|
+
".yarn",
|
17
|
+
".eslintrc",
|
18
|
+
".editorconfig",
|
19
|
+
".prettierrc",
|
20
|
+
".prettierignore",
|
21
|
+
".eslintignore",
|
22
|
+
".nycrc",
|
23
|
+
".babelrc",
|
24
|
+
".pnp.*",
|
25
|
+
"node_modules/.cache",
|
26
|
+
"node_modules/.bin",
|
27
|
+
"*.map",
|
28
|
+
".env.local",
|
29
|
+
".env.*.local",
|
30
|
+
"npm-debug.log*",
|
31
|
+
"yarn-debug.log*",
|
32
|
+
"yarn-error.log*",
|
33
|
+
"package-lock.json",
|
34
|
+
"yarn.lock",
|
35
|
+
"*.dxt",
|
36
|
+
"*.d.ts",
|
37
|
+
"*.tsbuildinfo",
|
38
|
+
"tsconfig.json",
|
39
|
+
];
|
40
|
+
/**
|
41
|
+
* Read and parse .dxtignore file patterns
|
42
|
+
*/
|
43
|
+
export function readDxtIgnorePatterns(baseDir) {
|
44
|
+
const dxtIgnorePath = join(baseDir, ".dxtignore");
|
45
|
+
if (!existsSync(dxtIgnorePath)) {
|
46
|
+
return [];
|
47
|
+
}
|
48
|
+
try {
|
49
|
+
const content = readFileSync(dxtIgnorePath, "utf-8");
|
50
|
+
return content
|
51
|
+
.split(/\r?\n/)
|
52
|
+
.map((line) => line.trim())
|
53
|
+
.filter((line) => line.length > 0 && !line.startsWith("#"));
|
54
|
+
}
|
55
|
+
catch (error) {
|
56
|
+
console.warn(`Warning: Could not read .dxtignore file: ${error instanceof Error ? error.message : "Unknown error"}`);
|
57
|
+
return [];
|
58
|
+
}
|
59
|
+
}
|
60
|
+
function buildIgnoreChecker(additionalPatterns) {
|
61
|
+
return ignore().add(EXCLUDE_PATTERNS).add(additionalPatterns);
|
62
|
+
}
|
63
|
+
/**
|
64
|
+
* Used for testing, calls the same methods as the other ignore checks
|
65
|
+
*/
|
66
|
+
export function shouldExclude(filePath, additionalPatterns = []) {
|
67
|
+
return buildIgnoreChecker(additionalPatterns).ignores(filePath);
|
68
|
+
}
|
69
|
+
export function getAllFiles(dirPath, baseDir = dirPath, fileList = {}, additionalPatterns = []) {
|
70
|
+
const files = readdirSync(dirPath);
|
71
|
+
const ignoreChecker = buildIgnoreChecker(additionalPatterns);
|
72
|
+
for (const file of files) {
|
73
|
+
const filePath = join(dirPath, file);
|
74
|
+
const relativePath = relative(baseDir, filePath);
|
75
|
+
if (ignoreChecker.ignores(relativePath)) {
|
76
|
+
continue;
|
77
|
+
}
|
78
|
+
const stat = statSync(filePath);
|
79
|
+
if (stat.isDirectory()) {
|
80
|
+
getAllFiles(filePath, baseDir, fileList, additionalPatterns);
|
81
|
+
}
|
82
|
+
else {
|
83
|
+
// Use forward slashes in zip file paths
|
84
|
+
const zipPath = relativePath.split(sep).join("/");
|
85
|
+
fileList[zipPath] = readFileSync(filePath);
|
86
|
+
}
|
87
|
+
}
|
88
|
+
return fileList;
|
89
|
+
}
|
90
|
+
export function getAllFilesWithCount(dirPath, baseDir = dirPath, fileList = {}, additionalPatterns = [], ignoredCount = 0) {
|
91
|
+
const files = readdirSync(dirPath);
|
92
|
+
const ignoreChecker = buildIgnoreChecker(additionalPatterns);
|
93
|
+
for (const file of files) {
|
94
|
+
const filePath = join(dirPath, file);
|
95
|
+
const relativePath = relative(baseDir, filePath);
|
96
|
+
if (ignoreChecker.ignores(relativePath)) {
|
97
|
+
ignoredCount++;
|
98
|
+
continue;
|
99
|
+
}
|
100
|
+
const stat = statSync(filePath);
|
101
|
+
if (stat.isDirectory()) {
|
102
|
+
const result = getAllFilesWithCount(filePath, baseDir, fileList, additionalPatterns, ignoredCount);
|
103
|
+
ignoredCount = result.ignoredCount;
|
104
|
+
}
|
105
|
+
else {
|
106
|
+
// Use forward slashes in zip file paths
|
107
|
+
const zipPath = relativePath.split(sep).join("/");
|
108
|
+
fileList[zipPath] = {
|
109
|
+
data: readFileSync(filePath),
|
110
|
+
mode: stat.mode,
|
111
|
+
};
|
112
|
+
}
|
113
|
+
}
|
114
|
+
return { files: fileList, ignoredCount };
|
115
|
+
}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import type { DxtSignatureInfo } from "../types.js";
|
2
|
+
/**
|
3
|
+
* Signs a DXT file with the given certificate and private key using PKCS#7
|
4
|
+
*
|
5
|
+
* @param dxtPath Path to the DXT file to sign
|
6
|
+
* @param certPath Path to the certificate file (PEM format)
|
7
|
+
* @param keyPath Path to the private key file (PEM format)
|
8
|
+
* @param intermediates Optional array of intermediate certificate paths
|
9
|
+
*/
|
10
|
+
export declare function signDxtFile(dxtPath: string, certPath: string, keyPath: string, intermediates?: string[]): void;
|
11
|
+
/**
|
12
|
+
* Verifies a signed DXT file using OS certificate store
|
13
|
+
*
|
14
|
+
* @param dxtPath Path to the signed DXT file
|
15
|
+
* @returns Signature information including verification status
|
16
|
+
*/
|
17
|
+
export declare function verifyDxtFile(dxtPath: string): Promise<DxtSignatureInfo>;
|
18
|
+
/**
|
19
|
+
* Extracts the signature block from a signed DXT file
|
20
|
+
*/
|
21
|
+
export declare function extractSignatureBlock(fileContent: Buffer): {
|
22
|
+
originalContent: Buffer;
|
23
|
+
pkcs7Signature?: Buffer;
|
24
|
+
};
|
25
|
+
/**
|
26
|
+
* Verifies certificate chain against OS trust store
|
27
|
+
*/
|
28
|
+
export declare function verifyCertificateChain(certificate: Buffer, intermediates?: Buffer[]): Promise<boolean>;
|
29
|
+
/**
|
30
|
+
* Removes signature from a DXT file
|
31
|
+
*/
|
32
|
+
export declare function unsignDxtFile(dxtPath: string): void;
|