@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
@@ -0,0 +1,333 @@
|
|
1
|
+
import { execFile } from "child_process";
|
2
|
+
import { readFileSync, writeFileSync } from "fs";
|
3
|
+
import { mkdtemp, rm, writeFile } from "fs/promises";
|
4
|
+
import forge from "node-forge";
|
5
|
+
import { tmpdir } from "os";
|
6
|
+
import { join } from "path";
|
7
|
+
import { promisify } from "util";
|
8
|
+
// Signature block markers
|
9
|
+
const SIGNATURE_HEADER = "DXT_SIG_V1";
|
10
|
+
const SIGNATURE_FOOTER = "DXT_SIG_END";
|
11
|
+
const execFileAsync = promisify(execFile);
|
12
|
+
/**
|
13
|
+
* Signs a DXT file with the given certificate and private key using PKCS#7
|
14
|
+
*
|
15
|
+
* @param dxtPath Path to the DXT file to sign
|
16
|
+
* @param certPath Path to the certificate file (PEM format)
|
17
|
+
* @param keyPath Path to the private key file (PEM format)
|
18
|
+
* @param intermediates Optional array of intermediate certificate paths
|
19
|
+
*/
|
20
|
+
export function signDxtFile(dxtPath, certPath, keyPath, intermediates) {
|
21
|
+
// Read the original DXT file
|
22
|
+
const dxtContent = readFileSync(dxtPath);
|
23
|
+
// Read certificate and key
|
24
|
+
const certificatePem = readFileSync(certPath, "utf-8");
|
25
|
+
const privateKeyPem = readFileSync(keyPath, "utf-8");
|
26
|
+
// Read intermediate certificates if provided
|
27
|
+
const intermediatePems = intermediates?.map((path) => readFileSync(path, "utf-8"));
|
28
|
+
// Create PKCS#7 signed data
|
29
|
+
const p7 = forge.pkcs7.createSignedData();
|
30
|
+
p7.content = forge.util.createBuffer(dxtContent);
|
31
|
+
// Parse and add certificates
|
32
|
+
const signingCert = forge.pki.certificateFromPem(certificatePem);
|
33
|
+
const privateKey = forge.pki.privateKeyFromPem(privateKeyPem);
|
34
|
+
p7.addCertificate(signingCert);
|
35
|
+
// Add intermediate certificates
|
36
|
+
if (intermediatePems) {
|
37
|
+
for (const pem of intermediatePems) {
|
38
|
+
p7.addCertificate(forge.pki.certificateFromPem(pem));
|
39
|
+
}
|
40
|
+
}
|
41
|
+
// Add signer
|
42
|
+
p7.addSigner({
|
43
|
+
key: privateKey,
|
44
|
+
certificate: signingCert,
|
45
|
+
digestAlgorithm: forge.pki.oids.sha256,
|
46
|
+
authenticatedAttributes: [
|
47
|
+
{
|
48
|
+
type: forge.pki.oids.contentType,
|
49
|
+
value: forge.pki.oids.data,
|
50
|
+
},
|
51
|
+
{
|
52
|
+
type: forge.pki.oids.messageDigest,
|
53
|
+
// Value will be auto-populated
|
54
|
+
},
|
55
|
+
{
|
56
|
+
type: forge.pki.oids.signingTime,
|
57
|
+
// Value will be auto-populated with current time
|
58
|
+
},
|
59
|
+
],
|
60
|
+
});
|
61
|
+
// Sign with detached signature
|
62
|
+
p7.sign({ detached: true });
|
63
|
+
// Convert to DER format
|
64
|
+
const asn1 = forge.asn1.toDer(p7.toAsn1());
|
65
|
+
const pkcs7Signature = Buffer.from(asn1.getBytes(), "binary");
|
66
|
+
// Create signature block with PKCS#7 data
|
67
|
+
const signatureBlock = createSignatureBlock(pkcs7Signature);
|
68
|
+
// Append signature block to DXT file
|
69
|
+
const signedContent = Buffer.concat([dxtContent, signatureBlock]);
|
70
|
+
writeFileSync(dxtPath, signedContent);
|
71
|
+
}
|
72
|
+
/**
|
73
|
+
* Verifies a signed DXT file using OS certificate store
|
74
|
+
*
|
75
|
+
* @param dxtPath Path to the signed DXT file
|
76
|
+
* @returns Signature information including verification status
|
77
|
+
*/
|
78
|
+
export async function verifyDxtFile(dxtPath) {
|
79
|
+
try {
|
80
|
+
const fileContent = readFileSync(dxtPath);
|
81
|
+
// Find and extract signature block
|
82
|
+
const { originalContent, pkcs7Signature } = extractSignatureBlock(fileContent);
|
83
|
+
if (!pkcs7Signature) {
|
84
|
+
return { status: "unsigned" };
|
85
|
+
}
|
86
|
+
// Parse PKCS#7 signature
|
87
|
+
const asn1 = forge.asn1.fromDer(pkcs7Signature.toString("binary"));
|
88
|
+
const p7Message = forge.pkcs7.messageFromAsn1(asn1);
|
89
|
+
// Verify it's signed data and cast to correct type
|
90
|
+
if (!("type" in p7Message) ||
|
91
|
+
p7Message.type !== forge.pki.oids.signedData) {
|
92
|
+
return { status: "unsigned" };
|
93
|
+
}
|
94
|
+
// Now we know it's PkcsSignedData. The types are incorrect, so we'll
|
95
|
+
// fix them there
|
96
|
+
const p7 = p7Message;
|
97
|
+
// Extract certificates from PKCS#7
|
98
|
+
const certificates = p7.certificates || [];
|
99
|
+
if (certificates.length === 0) {
|
100
|
+
return { status: "unsigned" };
|
101
|
+
}
|
102
|
+
// Get the signing certificate (first one)
|
103
|
+
const signingCert = certificates[0];
|
104
|
+
// Verify PKCS#7 signature
|
105
|
+
const contentBuf = forge.util.createBuffer(originalContent);
|
106
|
+
try {
|
107
|
+
p7.verify({ authenticatedAttributes: true });
|
108
|
+
// Also verify the content matches
|
109
|
+
const signerInfos = p7.signerInfos;
|
110
|
+
const signerInfo = signerInfos?.[0];
|
111
|
+
if (signerInfo) {
|
112
|
+
const md = forge.md.sha256.create();
|
113
|
+
md.update(contentBuf.getBytes());
|
114
|
+
const digest = md.digest().getBytes();
|
115
|
+
// Find the message digest attribute
|
116
|
+
let messageDigest = null;
|
117
|
+
for (const attr of signerInfo.authenticatedAttributes) {
|
118
|
+
if (attr.type === forge.pki.oids.messageDigest) {
|
119
|
+
messageDigest = attr.value;
|
120
|
+
break;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
if (!messageDigest || messageDigest !== digest) {
|
124
|
+
return { status: "unsigned" };
|
125
|
+
}
|
126
|
+
}
|
127
|
+
}
|
128
|
+
catch (error) {
|
129
|
+
return { status: "unsigned" };
|
130
|
+
}
|
131
|
+
// Convert forge certificate to PEM for OS verification
|
132
|
+
const certPem = forge.pki.certificateToPem(signingCert);
|
133
|
+
const intermediatePems = certificates
|
134
|
+
.slice(1)
|
135
|
+
.map((cert) => Buffer.from(forge.pki.certificateToPem(cert)));
|
136
|
+
// Verify certificate chain against OS trust store
|
137
|
+
const chainValid = await verifyCertificateChain(Buffer.from(certPem), intermediatePems);
|
138
|
+
if (!chainValid) {
|
139
|
+
// Signature is valid but certificate is not trusted
|
140
|
+
return { status: "unsigned" };
|
141
|
+
}
|
142
|
+
// Extract certificate info
|
143
|
+
const isSelfSigned = signingCert.issuer.getField("CN")?.value ===
|
144
|
+
signingCert.subject.getField("CN")?.value;
|
145
|
+
return {
|
146
|
+
status: isSelfSigned ? "self-signed" : "signed",
|
147
|
+
publisher: signingCert.subject.getField("CN")?.value || "Unknown",
|
148
|
+
issuer: signingCert.issuer.getField("CN")?.value || "Unknown",
|
149
|
+
valid_from: signingCert.validity.notBefore.toISOString(),
|
150
|
+
valid_to: signingCert.validity.notAfter.toISOString(),
|
151
|
+
fingerprint: forge.md.sha256
|
152
|
+
.create()
|
153
|
+
.update(forge.asn1.toDer(forge.pki.certificateToAsn1(signingCert)).getBytes())
|
154
|
+
.digest()
|
155
|
+
.toHex(),
|
156
|
+
};
|
157
|
+
}
|
158
|
+
catch (error) {
|
159
|
+
throw new Error(`Failed to verify DXT file: ${error}`);
|
160
|
+
}
|
161
|
+
}
|
162
|
+
/**
|
163
|
+
* Creates a signature block buffer with PKCS#7 signature
|
164
|
+
*/
|
165
|
+
function createSignatureBlock(pkcs7Signature) {
|
166
|
+
const parts = [];
|
167
|
+
// Header
|
168
|
+
parts.push(Buffer.from(SIGNATURE_HEADER, "utf-8"));
|
169
|
+
// PKCS#7 signature length and data
|
170
|
+
const sigLengthBuffer = Buffer.alloc(4);
|
171
|
+
sigLengthBuffer.writeUInt32LE(pkcs7Signature.length, 0);
|
172
|
+
parts.push(sigLengthBuffer);
|
173
|
+
parts.push(pkcs7Signature);
|
174
|
+
// Footer
|
175
|
+
parts.push(Buffer.from(SIGNATURE_FOOTER, "utf-8"));
|
176
|
+
return Buffer.concat(parts);
|
177
|
+
}
|
178
|
+
/**
|
179
|
+
* Extracts the signature block from a signed DXT file
|
180
|
+
*/
|
181
|
+
export function extractSignatureBlock(fileContent) {
|
182
|
+
// Look for signature footer at the end
|
183
|
+
const footerBytes = Buffer.from(SIGNATURE_FOOTER, "utf-8");
|
184
|
+
const footerIndex = fileContent.lastIndexOf(footerBytes);
|
185
|
+
if (footerIndex === -1) {
|
186
|
+
return { originalContent: fileContent };
|
187
|
+
}
|
188
|
+
// Look for signature header before footer
|
189
|
+
const headerBytes = Buffer.from(SIGNATURE_HEADER, "utf-8");
|
190
|
+
let headerIndex = -1;
|
191
|
+
// Search backwards from footer
|
192
|
+
for (let i = footerIndex - 1; i >= 0; i--) {
|
193
|
+
if (fileContent.slice(i, i + headerBytes.length).equals(headerBytes)) {
|
194
|
+
headerIndex = i;
|
195
|
+
break;
|
196
|
+
}
|
197
|
+
}
|
198
|
+
if (headerIndex === -1) {
|
199
|
+
return { originalContent: fileContent };
|
200
|
+
}
|
201
|
+
// Extract original content (everything before signature block)
|
202
|
+
const originalContent = fileContent.slice(0, headerIndex);
|
203
|
+
// Parse signature block
|
204
|
+
let offset = headerIndex + headerBytes.length;
|
205
|
+
try {
|
206
|
+
// Read PKCS#7 signature length
|
207
|
+
const sigLength = fileContent.readUInt32LE(offset);
|
208
|
+
offset += 4;
|
209
|
+
// Read PKCS#7 signature
|
210
|
+
const pkcs7Signature = fileContent.slice(offset, offset + sigLength);
|
211
|
+
return {
|
212
|
+
originalContent,
|
213
|
+
pkcs7Signature,
|
214
|
+
};
|
215
|
+
}
|
216
|
+
catch {
|
217
|
+
return { originalContent: fileContent };
|
218
|
+
}
|
219
|
+
}
|
220
|
+
/**
|
221
|
+
* Verifies certificate chain against OS trust store
|
222
|
+
*/
|
223
|
+
export async function verifyCertificateChain(certificate, intermediates) {
|
224
|
+
let tempDir = null;
|
225
|
+
try {
|
226
|
+
tempDir = await mkdtemp(join(tmpdir(), "dxt-verify-"));
|
227
|
+
const certChainPath = join(tempDir, "chain.pem");
|
228
|
+
const certChain = [certificate, ...(intermediates || [])].join("\n");
|
229
|
+
await writeFile(certChainPath, certChain);
|
230
|
+
// Platform-specific verification
|
231
|
+
if (process.platform === "darwin") {
|
232
|
+
try {
|
233
|
+
await execFileAsync("security", [
|
234
|
+
"verify-cert",
|
235
|
+
"-c",
|
236
|
+
certChainPath,
|
237
|
+
"-p",
|
238
|
+
"codeSign",
|
239
|
+
]);
|
240
|
+
return true;
|
241
|
+
}
|
242
|
+
catch (error) {
|
243
|
+
return false;
|
244
|
+
}
|
245
|
+
}
|
246
|
+
else if (process.platform === "win32") {
|
247
|
+
const psCommand = `
|
248
|
+
$ErrorActionPreference = 'Stop'
|
249
|
+
$certCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
|
250
|
+
$certCollection.Import('${certChainPath}')
|
251
|
+
|
252
|
+
if ($certCollection.Count -eq 0) {
|
253
|
+
Write-Error 'No certificates found'
|
254
|
+
exit 1
|
255
|
+
}
|
256
|
+
|
257
|
+
$leafCert = $certCollection[0]
|
258
|
+
$chain = New-Object System.Security.Cryptography.X509Certificates.X509Chain
|
259
|
+
|
260
|
+
# Enable revocation checking
|
261
|
+
$chain.ChainPolicy.RevocationMode = 'Online'
|
262
|
+
$chain.ChainPolicy.RevocationFlag = 'EntireChain'
|
263
|
+
$chain.ChainPolicy.UrlRetrievalTimeout = New-TimeSpan -Seconds 30
|
264
|
+
|
265
|
+
# Add code signing application policy
|
266
|
+
$codeSignOid = New-Object System.Security.Cryptography.Oid '1.3.6.1.5.5.7.3.3'
|
267
|
+
$chain.ChainPolicy.ApplicationPolicy.Add($codeSignOid)
|
268
|
+
|
269
|
+
# Add intermediate certificates to extra store
|
270
|
+
for ($i = 1; $i -lt $certCollection.Count; $i++) {
|
271
|
+
[void]$chain.ChainPolicy.ExtraStore.Add($certCollection[$i])
|
272
|
+
}
|
273
|
+
|
274
|
+
# Build and validate chain
|
275
|
+
$result = $chain.Build($leafCert)
|
276
|
+
|
277
|
+
if ($result) {
|
278
|
+
'Valid'
|
279
|
+
} else {
|
280
|
+
$chain.ChainStatus | ForEach-Object {
|
281
|
+
Write-Error "$($_.Status): $($_.StatusInformation)"
|
282
|
+
}
|
283
|
+
exit 1
|
284
|
+
}
|
285
|
+
`.trim();
|
286
|
+
const { stdout } = await execFileAsync("powershell.exe", [
|
287
|
+
"-NoProfile",
|
288
|
+
"-NonInteractive",
|
289
|
+
"-Command",
|
290
|
+
psCommand,
|
291
|
+
]);
|
292
|
+
return stdout.includes("Valid");
|
293
|
+
}
|
294
|
+
else {
|
295
|
+
// Linux: Use openssl
|
296
|
+
try {
|
297
|
+
await execFileAsync("openssl", [
|
298
|
+
"verify",
|
299
|
+
"-purpose",
|
300
|
+
"codesigning",
|
301
|
+
"-CApath",
|
302
|
+
"/etc/ssl/certs",
|
303
|
+
certChainPath,
|
304
|
+
]);
|
305
|
+
return true;
|
306
|
+
}
|
307
|
+
catch (error) {
|
308
|
+
return false;
|
309
|
+
}
|
310
|
+
}
|
311
|
+
}
|
312
|
+
catch (error) {
|
313
|
+
return false;
|
314
|
+
}
|
315
|
+
finally {
|
316
|
+
if (tempDir) {
|
317
|
+
try {
|
318
|
+
await rm(tempDir, { recursive: true, force: true });
|
319
|
+
}
|
320
|
+
catch {
|
321
|
+
// Ignore cleanup errors
|
322
|
+
}
|
323
|
+
}
|
324
|
+
}
|
325
|
+
}
|
326
|
+
/**
|
327
|
+
* Removes signature from a DXT file
|
328
|
+
*/
|
329
|
+
export function unsignDxtFile(dxtPath) {
|
330
|
+
const fileContent = readFileSync(dxtPath);
|
331
|
+
const { originalContent } = extractSignatureBlock(fileContent);
|
332
|
+
writeFileSync(dxtPath, originalContent);
|
333
|
+
}
|
@@ -0,0 +1,124 @@
|
|
1
|
+
import { existsSync, readFileSync, statSync } from "fs";
|
2
|
+
import * as fs from "fs/promises";
|
3
|
+
import { DestroyerOfModules } from "galactus";
|
4
|
+
import * as os from "os";
|
5
|
+
import { join, resolve } from "path";
|
6
|
+
import prettyBytes from "pretty-bytes";
|
7
|
+
import { unpackExtension } from "../cli/unpack.js";
|
8
|
+
import { DxtManifestSchema } from "../schemas.js";
|
9
|
+
import { DxtManifestSchema as LooseDxtManifestSchema } from "../schemas-loose.js";
|
10
|
+
export function validateManifest(inputPath) {
|
11
|
+
try {
|
12
|
+
const resolvedPath = resolve(inputPath);
|
13
|
+
let manifestPath = resolvedPath;
|
14
|
+
// If input is a directory, look for manifest.json inside it
|
15
|
+
if (existsSync(resolvedPath) && statSync(resolvedPath).isDirectory()) {
|
16
|
+
manifestPath = join(resolvedPath, "manifest.json");
|
17
|
+
}
|
18
|
+
const manifestContent = readFileSync(manifestPath, "utf-8");
|
19
|
+
const manifestData = JSON.parse(manifestContent);
|
20
|
+
const result = DxtManifestSchema.safeParse(manifestData);
|
21
|
+
if (result.success) {
|
22
|
+
console.log("Manifest is valid!");
|
23
|
+
return true;
|
24
|
+
}
|
25
|
+
else {
|
26
|
+
console.log("ERROR: Manifest validation failed:\n");
|
27
|
+
result.error.issues.forEach((issue) => {
|
28
|
+
const path = issue.path.join(".");
|
29
|
+
console.log(` - ${path ? `${path}: ` : ""}${issue.message}`);
|
30
|
+
});
|
31
|
+
return false;
|
32
|
+
}
|
33
|
+
}
|
34
|
+
catch (error) {
|
35
|
+
if (error instanceof Error) {
|
36
|
+
if (error.message.includes("ENOENT")) {
|
37
|
+
console.error(`ERROR: File not found: ${inputPath}`);
|
38
|
+
if (existsSync(resolve(inputPath)) &&
|
39
|
+
statSync(resolve(inputPath)).isDirectory()) {
|
40
|
+
console.error(` (No manifest.json found in directory)`);
|
41
|
+
}
|
42
|
+
}
|
43
|
+
else if (error.message.includes("JSON")) {
|
44
|
+
console.error(`ERROR: Invalid JSON in manifest file: ${error.message}`);
|
45
|
+
}
|
46
|
+
else {
|
47
|
+
console.error(`ERROR: Error reading manifest: ${error.message}`);
|
48
|
+
}
|
49
|
+
}
|
50
|
+
else {
|
51
|
+
console.error("ERROR: Unknown error occurred");
|
52
|
+
}
|
53
|
+
return false;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
export async function cleanDxt(inputPath) {
|
57
|
+
const tmpDir = await fs.mkdtemp(resolve(os.tmpdir(), "dxt-clean-"));
|
58
|
+
const dxtPath = resolve(tmpDir, "in.dxt");
|
59
|
+
const unpackPath = resolve(tmpDir, "out");
|
60
|
+
console.log(" -- Cleaning DXT...");
|
61
|
+
try {
|
62
|
+
await fs.copyFile(inputPath, dxtPath);
|
63
|
+
console.log(" -- Unpacking DXT...");
|
64
|
+
await unpackExtension({ dxtPath, silent: true, outputDir: unpackPath });
|
65
|
+
const manifestPath = resolve(unpackPath, "manifest.json");
|
66
|
+
const originalManifest = await fs.readFile(manifestPath, "utf-8");
|
67
|
+
const manifestData = JSON.parse(originalManifest);
|
68
|
+
const result = LooseDxtManifestSchema.safeParse(manifestData);
|
69
|
+
if (!result.success) {
|
70
|
+
throw new Error(`Unrecoverable manifest issues, please run "dxt validate"`);
|
71
|
+
}
|
72
|
+
await fs.writeFile(manifestPath, JSON.stringify(result.data, null, 2));
|
73
|
+
if (originalManifest.trim() !==
|
74
|
+
(await fs.readFile(manifestPath, "utf8")).trim()) {
|
75
|
+
console.log(" -- Update manifest to be valid per DXT schema");
|
76
|
+
}
|
77
|
+
else {
|
78
|
+
console.log(" -- Manifest already valid per DXT schema");
|
79
|
+
}
|
80
|
+
const nodeModulesPath = resolve(unpackPath, "node_modules");
|
81
|
+
if (existsSync(nodeModulesPath)) {
|
82
|
+
console.log(" -- node_modules found, deleting development dependencies");
|
83
|
+
const destroyer = new DestroyerOfModules({
|
84
|
+
rootDirectory: unpackPath,
|
85
|
+
});
|
86
|
+
try {
|
87
|
+
await destroyer.destroy();
|
88
|
+
}
|
89
|
+
catch (error) {
|
90
|
+
// If modules have already been deleted in a previous clean, the walker
|
91
|
+
// will fail when it can't find required dependencies. This is expected
|
92
|
+
// and safe to ignore.
|
93
|
+
if (error instanceof Error &&
|
94
|
+
error.message.includes("Failed to locate module")) {
|
95
|
+
console.log(" -- Some modules already removed, skipping remaining cleanup");
|
96
|
+
}
|
97
|
+
else {
|
98
|
+
throw error;
|
99
|
+
}
|
100
|
+
}
|
101
|
+
console.log(" -- Removed development dependencies from node_modules");
|
102
|
+
}
|
103
|
+
else {
|
104
|
+
console.log(" -- No node_modules, not pruning");
|
105
|
+
}
|
106
|
+
const before = await fs.stat(inputPath);
|
107
|
+
const { packExtension } = await import("../cli/pack.js");
|
108
|
+
await packExtension({
|
109
|
+
extensionPath: unpackPath,
|
110
|
+
outputPath: inputPath,
|
111
|
+
silent: true,
|
112
|
+
});
|
113
|
+
const after = await fs.stat(inputPath);
|
114
|
+
console.log("\nClean Complete:");
|
115
|
+
console.log("Before:", prettyBytes(before.size));
|
116
|
+
console.log("After:", prettyBytes(after.size));
|
117
|
+
}
|
118
|
+
finally {
|
119
|
+
await fs.rm(tmpDir, {
|
120
|
+
recursive: true,
|
121
|
+
force: true,
|
122
|
+
});
|
123
|
+
}
|
124
|
+
}
|
package/dist/node.d.ts
ADDED
package/dist/node.js
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
// Node.js-specific exports
|
2
|
+
export * from "./node/files.js";
|
3
|
+
export * from "./node/sign.js";
|
4
|
+
export * from "./node/validate.js";
|
5
|
+
// Include all shared exports
|
6
|
+
export * from "./schemas.js";
|
7
|
+
export * from "./shared/config.js";
|
8
|
+
export * from "./types.js";
|