@involvex/msix-packager-cli 1.4.1
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/README.md +360 -0
- package/package.json +57 -0
- package/src/certificates.js +320 -0
- package/src/cli.js +383 -0
- package/src/constants.js +140 -0
- package/src/index.js +414 -0
- package/src/manifest.js +389 -0
- package/src/package.js +909 -0
- package/src/sea-handler-new.js +301 -0
- package/src/sea-handler.js +1124 -0
- package/src/utils.js +292 -0
- package/src/validation.js +228 -0
package/src/constants.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants for MSIX package creation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const CONSTANTS = {
|
|
6
|
+
// Default configuration values
|
|
7
|
+
DEFAULT_VERSION: "1.0.0.0",
|
|
8
|
+
DEFAULT_ARCHITECTURE: "x64",
|
|
9
|
+
DEFAULT_EXECUTABLE: "node.exe",
|
|
10
|
+
DEFAULT_TIMESTAMP_URL: "http://timestamp.digicert.com",
|
|
11
|
+
DEFAULT_BACKGROUND_COLOR: "transparent",
|
|
12
|
+
|
|
13
|
+
// Package requirements
|
|
14
|
+
MIN_VERSION_PARTS: 4,
|
|
15
|
+
MAX_PACKAGE_NAME_LENGTH: 50,
|
|
16
|
+
|
|
17
|
+
// File paths and extensions
|
|
18
|
+
MANIFEST_FILE: "AppxManifest.xml",
|
|
19
|
+
ASSETS_FOLDER: "Assets",
|
|
20
|
+
APP_FOLDER: "app",
|
|
21
|
+
|
|
22
|
+
// Asset file names
|
|
23
|
+
ASSET_FILES: [
|
|
24
|
+
"Square150x150Logo.png",
|
|
25
|
+
"Square44x44Logo.png",
|
|
26
|
+
"StoreLogo.png",
|
|
27
|
+
"Wide310x150Logo.png",
|
|
28
|
+
"SplashScreen.png",
|
|
29
|
+
],
|
|
30
|
+
|
|
31
|
+
// Windows SDK paths (ordered by preference)
|
|
32
|
+
WINDOWS_SDK_PATHS: [
|
|
33
|
+
"C:\\Program Files (x86)\\Windows Kits\\10\\bin\\10.0.26100.0\\x64",
|
|
34
|
+
"C:\\Program Files (x86)\\Windows Kits\\10\\bin\\10.0.22621.0\\x64",
|
|
35
|
+
"C:\\Program Files (x86)\\Windows Kits\\10\\bin\\10.0.19041.0\\x64",
|
|
36
|
+
"C:\\Program Files (x86)\\Windows Kits\\10\\bin\\10.0.18362.0\\x64",
|
|
37
|
+
"C:\\Program Files (x86)\\Windows Kits\\10\\bin\\10.0.17763.0\\x64",
|
|
38
|
+
"C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v10.0A\\bin\\NETFX 4.8 Tools\\x64",
|
|
39
|
+
],
|
|
40
|
+
|
|
41
|
+
// Certificate stores to search
|
|
42
|
+
CERTIFICATE_STORES: [
|
|
43
|
+
{ location: "CurrentUser", store: "My" },
|
|
44
|
+
{ location: "CurrentUser", store: "TrustedPublisher" },
|
|
45
|
+
{ location: "CurrentUser", store: "TrustedPeople" },
|
|
46
|
+
{ location: "LocalMachine", store: "My" },
|
|
47
|
+
{ location: "LocalMachine", store: "TrustedPublisher" },
|
|
48
|
+
{ location: "LocalMachine", store: "TrustedPeople" },
|
|
49
|
+
],
|
|
50
|
+
|
|
51
|
+
// Default capabilities
|
|
52
|
+
DEFAULT_CAPABILITIES: ["internetClient", "runFullTrust"],
|
|
53
|
+
|
|
54
|
+
// Supported architectures
|
|
55
|
+
SUPPORTED_ARCHITECTURES: ["x64", "x86", "arm64"],
|
|
56
|
+
|
|
57
|
+
// Validation patterns
|
|
58
|
+
PUBLISHER_PATTERN: /^CN=/,
|
|
59
|
+
VERSION_PATTERN: /^\d+\.\d+\.\d+\.\d+$/,
|
|
60
|
+
|
|
61
|
+
// Files and directories to exclude when copying
|
|
62
|
+
COPY_EXCLUDE_PATTERNS: [
|
|
63
|
+
"node_modules/.cache",
|
|
64
|
+
".git",
|
|
65
|
+
".gitignore",
|
|
66
|
+
".npm",
|
|
67
|
+
".nyc_output",
|
|
68
|
+
"coverage",
|
|
69
|
+
"*.log",
|
|
70
|
+
"npm-debug.log*",
|
|
71
|
+
"yarn-debug.log*",
|
|
72
|
+
"yarn-error.log*",
|
|
73
|
+
".DS_Store",
|
|
74
|
+
"Thumbs.db",
|
|
75
|
+
],
|
|
76
|
+
|
|
77
|
+
// Configuration file name
|
|
78
|
+
CONFIG_FILENAME: "msix-config.json",
|
|
79
|
+
|
|
80
|
+
// Package size limits
|
|
81
|
+
MAX_PACKAGE_SIZE: 500 * 1024 * 1024, // 500MB
|
|
82
|
+
|
|
83
|
+
// Default 1x1 transparent PNG (base64)
|
|
84
|
+
DEFAULT_PNG_BASE64:
|
|
85
|
+
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChAI9jU77zgAAAABJRU5ErkJggg==",
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Error types for better error handling
|
|
89
|
+
class MsixError extends Error {
|
|
90
|
+
constructor(message, code, details) {
|
|
91
|
+
super(message);
|
|
92
|
+
this.name = "MsixError";
|
|
93
|
+
this.code = code;
|
|
94
|
+
this.details = details;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
class ValidationError extends MsixError {
|
|
99
|
+
constructor(field, value, expected) {
|
|
100
|
+
super(
|
|
101
|
+
`Validation failed for field '${field}': expected ${expected}, got ${value}`,
|
|
102
|
+
"VALIDATION_ERROR",
|
|
103
|
+
{ field, value, expected },
|
|
104
|
+
);
|
|
105
|
+
this.name = "ValidationError";
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
class ToolNotFoundError extends MsixError {
|
|
110
|
+
constructor(tool, suggestions = []) {
|
|
111
|
+
super(`Required tool '${tool}' not found`, "TOOL_NOT_FOUND", {
|
|
112
|
+
tool,
|
|
113
|
+
suggestions,
|
|
114
|
+
});
|
|
115
|
+
this.name = "ToolNotFoundError";
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
class CertificateError extends MsixError {
|
|
120
|
+
constructor(message, details) {
|
|
121
|
+
super(message, "CERTIFICATE_ERROR", details);
|
|
122
|
+
this.name = "CertificateError";
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
class SigningError extends MsixError {
|
|
127
|
+
constructor(message, details) {
|
|
128
|
+
super(message, "SIGNING_ERROR", details);
|
|
129
|
+
this.name = "SigningError";
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module.exports = {
|
|
134
|
+
CONSTANTS,
|
|
135
|
+
MSIXError: MsixError,
|
|
136
|
+
ValidationError,
|
|
137
|
+
ToolNotFoundError,
|
|
138
|
+
CertificateError,
|
|
139
|
+
SigningError,
|
|
140
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const fs = require("fs-extra");
|
|
3
|
+
const chalk = require("chalk");
|
|
4
|
+
|
|
5
|
+
// Import modular components
|
|
6
|
+
const {
|
|
7
|
+
validateConfig,
|
|
8
|
+
validatePaths,
|
|
9
|
+
sanitizeConfig,
|
|
10
|
+
validateSigningConfig,
|
|
11
|
+
} = require("./validation");
|
|
12
|
+
const {
|
|
13
|
+
getTempDir,
|
|
14
|
+
cleanup,
|
|
15
|
+
readPackageJson,
|
|
16
|
+
validateRequiredTools,
|
|
17
|
+
getSystemInfo,
|
|
18
|
+
} = require("./utils");
|
|
19
|
+
const { findCodeSigningCertificates } = require("./certificates");
|
|
20
|
+
const {
|
|
21
|
+
createMsixPackage: createPackage,
|
|
22
|
+
signMsixPackage: signPackage,
|
|
23
|
+
preparePackageDirectory,
|
|
24
|
+
validatePackage,
|
|
25
|
+
} = require("./package");
|
|
26
|
+
const { CONSTANTS, MSIXError, ValidationError } = require("./constants");
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Creates an MSIX package from a Node.js application
|
|
30
|
+
*
|
|
31
|
+
* @param {Object} config - Configuration object
|
|
32
|
+
* @param {string} config.inputPath - Path to the Node.js application directory
|
|
33
|
+
* @param {string} config.outputPath - Output directory for the MSIX package
|
|
34
|
+
* @param {string} config.appName - Application name (required)
|
|
35
|
+
* @param {string} config.publisher - Publisher name in format "CN=PublisherName" (required)
|
|
36
|
+
* @param {string} config.version - Application version in format "x.x.x.x" (optional, defaults to "1.0.0.0")
|
|
37
|
+
* @param {string} config.description - Application description (optional)
|
|
38
|
+
* @param {string} config.executable - Main executable file (optional, defaults to "node.exe")
|
|
39
|
+
* @param {string} config.icon - Path to application icon file (optional)
|
|
40
|
+
* @param {string} config.architecture - Target architecture (optional, defaults to "x64")
|
|
41
|
+
* @param {string} config.displayName - Display name shown to users (optional, defaults to appName)
|
|
42
|
+
* @param {string} config.packageName - Package identity name (optional, auto-generated if not provided)
|
|
43
|
+
* @param {Array<string>} config.capabilities - Application capabilities (optional, defaults to ["internetClient"])
|
|
44
|
+
* @param {string} config.backgroundColor - Background color for tiles (optional, defaults to "transparent")
|
|
45
|
+
*
|
|
46
|
+
* @param {boolean} config.sign - Whether to sign the MSIX package (optional, defaults to true)
|
|
47
|
+
* @param {string} config.certificateThumbprint - Certificate thumbprint for signing (optional)
|
|
48
|
+
* @param {string} config.certificateSubject - Certificate subject name for signing (optional)
|
|
49
|
+
* @param {string} config.certificatePath - Path to PFX certificate file (optional)
|
|
50
|
+
* @param {string} config.certificatePassword - Password for PFX certificate (optional)
|
|
51
|
+
* @param {string} config.timestampUrl - Timestamp server URL (optional)
|
|
52
|
+
*
|
|
53
|
+
* @returns {Promise<Object>} Result object containing package information
|
|
54
|
+
* @throws {ValidationError} When configuration is invalid
|
|
55
|
+
* @throws {MSIXError} When package creation fails
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* const { createMsixPackage } = require('node-msix');
|
|
59
|
+
*
|
|
60
|
+
* const result = await createMsixPackage({
|
|
61
|
+
* inputPath: './my-node-app',
|
|
62
|
+
* outputPath: './dist',
|
|
63
|
+
* appName: 'My Node App',
|
|
64
|
+
* publisher: 'CN=My Company',
|
|
65
|
+
* version: '1.0.0.0'
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* console.log('Package created:', result.packagePath);
|
|
69
|
+
*/
|
|
70
|
+
async function createMsixPackage(config) {
|
|
71
|
+
const cleanupPaths = [];
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
// Validate system requirements
|
|
75
|
+
console.log(chalk.blue("🔍 Validating system requirements..."));
|
|
76
|
+
validateRequiredTools();
|
|
77
|
+
|
|
78
|
+
// Validate and sanitize configuration
|
|
79
|
+
console.log(chalk.blue("✅ Validating configuration..."));
|
|
80
|
+
validateConfig(config);
|
|
81
|
+
await validatePaths(config);
|
|
82
|
+
|
|
83
|
+
const sanitizedConfig = sanitizeConfig(config);
|
|
84
|
+
|
|
85
|
+
// Set default signing configuration
|
|
86
|
+
const signingConfig = {
|
|
87
|
+
sign: sanitizedConfig.sign !== false, // Default to true
|
|
88
|
+
certificateThumbprint: sanitizedConfig.certificateThumbprint,
|
|
89
|
+
certificateSubject: sanitizedConfig.certificateSubject,
|
|
90
|
+
certificatePath: sanitizedConfig.certificatePath,
|
|
91
|
+
certificatePassword: sanitizedConfig.certificatePassword,
|
|
92
|
+
timestampUrl: sanitizedConfig.timestampUrl,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
validateSigningConfig(signingConfig);
|
|
96
|
+
|
|
97
|
+
// Read package.json from input directory
|
|
98
|
+
console.log(chalk.blue("📖 Reading package.json..."));
|
|
99
|
+
const packageJson = await readPackageJson(sanitizedConfig.inputPath);
|
|
100
|
+
|
|
101
|
+
// Create temporary directory
|
|
102
|
+
const tempDir = getTempDir("msix-package");
|
|
103
|
+
cleanupPaths.push(tempDir);
|
|
104
|
+
|
|
105
|
+
// Prepare package directory
|
|
106
|
+
const packageDir = await preparePackageDirectory(
|
|
107
|
+
sanitizedConfig,
|
|
108
|
+
tempDir,
|
|
109
|
+
packageJson,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// Validate prepared package
|
|
113
|
+
console.log(chalk.blue("🔍 Validating package contents..."));
|
|
114
|
+
const validation = await validatePackage(packageDir);
|
|
115
|
+
|
|
116
|
+
if (!validation.isValid) {
|
|
117
|
+
throw new ValidationError(
|
|
118
|
+
"package contents",
|
|
119
|
+
"valid package structure",
|
|
120
|
+
validation.issues.join("; "),
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (validation.warnings.length > 0) {
|
|
125
|
+
console.log(chalk.yellow("⚠️ Package validation warnings:"));
|
|
126
|
+
validation.warnings.forEach((warning) => {
|
|
127
|
+
console.log(chalk.yellow(` • ${warning}`));
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.log(chalk.green(`📦 Package size: ${validation.packageSize}`));
|
|
132
|
+
|
|
133
|
+
// Generate output filename
|
|
134
|
+
const packageName = sanitizedConfig.packageName.replace(
|
|
135
|
+
/[^a-zA-Z0-9.-]/g,
|
|
136
|
+
"",
|
|
137
|
+
);
|
|
138
|
+
const version = sanitizedConfig.version;
|
|
139
|
+
const architecture = sanitizedConfig.architecture;
|
|
140
|
+
const outputFilename = `${packageName}_${version}_${architecture}.msix`;
|
|
141
|
+
const outputPath = path.join(sanitizedConfig.outputPath, outputFilename);
|
|
142
|
+
|
|
143
|
+
// Create MSIX package
|
|
144
|
+
console.log(chalk.blue("🏗️ Creating MSIX package..."));
|
|
145
|
+
await createPackage(packageDir, outputPath);
|
|
146
|
+
|
|
147
|
+
let signedSuccessfully = false;
|
|
148
|
+
|
|
149
|
+
// Sign the package if requested
|
|
150
|
+
if (signingConfig.sign) {
|
|
151
|
+
try {
|
|
152
|
+
console.log(chalk.blue("🔐 Signing MSIX package..."));
|
|
153
|
+
signedSuccessfully = await signPackage(outputPath, signingConfig);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.log(
|
|
156
|
+
chalk.yellow(`⚠️ Package signing failed: ${error.message}`),
|
|
157
|
+
);
|
|
158
|
+
console.log(
|
|
159
|
+
chalk.yellow(
|
|
160
|
+
"The unsigned package has still been created successfully.",
|
|
161
|
+
),
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Clean up temporary files
|
|
167
|
+
console.log(chalk.blue("🧹 Cleaning up temporary files..."));
|
|
168
|
+
await cleanup(cleanupPaths);
|
|
169
|
+
|
|
170
|
+
const result = {
|
|
171
|
+
success: true,
|
|
172
|
+
packagePath: outputPath,
|
|
173
|
+
packageSize: validation.packageSize,
|
|
174
|
+
signed: signedSuccessfully,
|
|
175
|
+
config: {
|
|
176
|
+
appName: sanitizedConfig.appName,
|
|
177
|
+
version: sanitizedConfig.version,
|
|
178
|
+
publisher: sanitizedConfig.publisher,
|
|
179
|
+
architecture: sanitizedConfig.architecture,
|
|
180
|
+
packageName: sanitizedConfig.packageName,
|
|
181
|
+
},
|
|
182
|
+
systemInfo: getSystemInfo(),
|
|
183
|
+
timestamp: new Date().toISOString(),
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
console.log(chalk.green("🎉 MSIX package created successfully!"));
|
|
187
|
+
console.log(chalk.green(`📍 Location: ${outputPath}`));
|
|
188
|
+
|
|
189
|
+
if (signedSuccessfully) {
|
|
190
|
+
console.log(chalk.green("🔐 Package signed successfully"));
|
|
191
|
+
} else if (signingConfig.sign) {
|
|
192
|
+
console.log(chalk.yellow("⚠️ Package created but not signed"));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return result;
|
|
196
|
+
} catch (error) {
|
|
197
|
+
// Clean up on error
|
|
198
|
+
if (cleanupPaths.length > 0) {
|
|
199
|
+
try {
|
|
200
|
+
await cleanup(cleanupPaths);
|
|
201
|
+
} catch (cleanupError) {
|
|
202
|
+
console.warn(
|
|
203
|
+
chalk.yellow(`Warning: Cleanup failed: ${cleanupError.message}`),
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Re-throw the original error
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Signs an existing MSIX package
|
|
215
|
+
*
|
|
216
|
+
* @param {string} packagePath - Path to the MSIX package to sign
|
|
217
|
+
* @param {Object} signingConfig - Signing configuration
|
|
218
|
+
* @param {string} signingConfig.certificateThumbprint - Certificate thumbprint (optional)
|
|
219
|
+
* @param {string} signingConfig.certificateSubject - Certificate subject (optional)
|
|
220
|
+
* @param {string} signingConfig.certificatePath - Path to PFX certificate (optional)
|
|
221
|
+
* @param {string} signingConfig.certificatePassword - PFX password (optional)
|
|
222
|
+
* @param {string} signingConfig.timestampUrl - Timestamp server URL (optional)
|
|
223
|
+
*
|
|
224
|
+
* @returns {Promise<boolean>} True if signing was successful
|
|
225
|
+
* @throws {ValidationError} When signing configuration is invalid
|
|
226
|
+
* @throws {SigningError} When signing fails
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* const { signMsixPackage } = require('node-msix');
|
|
230
|
+
*
|
|
231
|
+
* const success = await signMsixPackage('./dist/MyApp.msix', {
|
|
232
|
+
* certificatePath: './certificates/mycert.pfx',
|
|
233
|
+
* certificatePassword: 'mypassword'
|
|
234
|
+
* });
|
|
235
|
+
*/
|
|
236
|
+
async function signMsixPackage(packagePath, signingConfig = {}) {
|
|
237
|
+
try {
|
|
238
|
+
console.log(chalk.blue("🔐 Signing MSIX package..."));
|
|
239
|
+
|
|
240
|
+
// Validate inputs
|
|
241
|
+
if (!packagePath || !(await fs.pathExists(packagePath))) {
|
|
242
|
+
throw new ValidationError(
|
|
243
|
+
"packagePath",
|
|
244
|
+
"existing MSIX file",
|
|
245
|
+
packagePath,
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Default signing configuration
|
|
250
|
+
const config = {
|
|
251
|
+
sign: true,
|
|
252
|
+
timestampUrl:
|
|
253
|
+
signingConfig.timestampUrl || CONSTANTS.DEFAULT_TIMESTAMP_URL,
|
|
254
|
+
...signingConfig,
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
validateSigningConfig(config);
|
|
258
|
+
|
|
259
|
+
const result = await signPackage(packagePath, config);
|
|
260
|
+
|
|
261
|
+
if (result) {
|
|
262
|
+
console.log(chalk.green("🔐 Package signed successfully"));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return result;
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error(chalk.red(`❌ Signing failed: ${error.message}`));
|
|
268
|
+
throw error;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Lists available code signing certificates on the system
|
|
274
|
+
*
|
|
275
|
+
* @returns {Promise<Array>} Array of certificate objects
|
|
276
|
+
* @throws {CertificateError} When certificate enumeration fails
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* const { listCertificates } = require('node-msix');
|
|
280
|
+
*
|
|
281
|
+
* const certificates = await listCertificates();
|
|
282
|
+
* certificates.forEach(cert => {
|
|
283
|
+
* console.log(`Subject: ${cert.subject}`);
|
|
284
|
+
* console.log(`Thumbprint: ${cert.thumbprint}`);
|
|
285
|
+
* console.log(`Valid: ${cert.isValid}`);
|
|
286
|
+
* });
|
|
287
|
+
*/
|
|
288
|
+
async function listCertificates() {
|
|
289
|
+
try {
|
|
290
|
+
console.log(chalk.blue("🔍 Searching for code signing certificates..."));
|
|
291
|
+
|
|
292
|
+
const certificates = await findCodeSigningCertificates();
|
|
293
|
+
|
|
294
|
+
if (certificates.length === 0) {
|
|
295
|
+
console.log(chalk.yellow("⚠️ No code signing certificates found"));
|
|
296
|
+
} else {
|
|
297
|
+
console.log(
|
|
298
|
+
chalk.green(`✅ Found ${certificates.length} certificate(s)`),
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return certificates;
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.error(chalk.red(`❌ Certificate search failed: ${error.message}`));
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Initializes a new MSIX configuration file
|
|
311
|
+
*
|
|
312
|
+
* @param {string} targetDir - Directory to create config file in (optional, defaults to current directory)
|
|
313
|
+
* @param {Object} options - Configuration options (optional)
|
|
314
|
+
* @returns {Promise<string>} Path to created config file
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* const { initConfig } = require('node-msix');
|
|
318
|
+
*
|
|
319
|
+
* const configPath = await initConfig('./my-app', {
|
|
320
|
+
* appName: 'My Application',
|
|
321
|
+
* publisher: 'CN=My Company'
|
|
322
|
+
* });
|
|
323
|
+
*/
|
|
324
|
+
async function initConfig(targetDir = process.cwd(), options = {}) {
|
|
325
|
+
try {
|
|
326
|
+
const configPath = path.join(targetDir, CONSTANTS.CONFIG_FILENAME);
|
|
327
|
+
|
|
328
|
+
// Check if config already exists
|
|
329
|
+
if (await fs.pathExists(configPath)) {
|
|
330
|
+
throw new ValidationError(
|
|
331
|
+
"config file",
|
|
332
|
+
"non-existing file",
|
|
333
|
+
"Config file already exists",
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Read package.json for defaults if it exists
|
|
338
|
+
let packageJson = {};
|
|
339
|
+
const packageJsonPath = path.join(targetDir, "package.json");
|
|
340
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
341
|
+
packageJson = await readPackageJson(targetDir);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Create default configuration
|
|
345
|
+
const config = {
|
|
346
|
+
appName: options.appName || packageJson.name || path.basename(targetDir),
|
|
347
|
+
publisher: options.publisher || "CN=DefaultPublisher",
|
|
348
|
+
version:
|
|
349
|
+
options.version ||
|
|
350
|
+
(packageJson.version ? `${packageJson.version}.0` : "1.0.0.0"),
|
|
351
|
+
description:
|
|
352
|
+
options.description ||
|
|
353
|
+
packageJson.description ||
|
|
354
|
+
"Node.js application packaged as MSIX",
|
|
355
|
+
executable: options.executable || CONSTANTS.DEFAULT_EXECUTABLE,
|
|
356
|
+
architecture: options.architecture || CONSTANTS.DEFAULT_ARCHITECTURE,
|
|
357
|
+
capabilities: options.capabilities || CONSTANTS.DEFAULT_CAPABILITIES,
|
|
358
|
+
sign: options.sign !== false,
|
|
359
|
+
|
|
360
|
+
// Paths (relative to config file)
|
|
361
|
+
inputPath: options.inputPath || ".",
|
|
362
|
+
outputPath: options.outputPath || "./dist",
|
|
363
|
+
icon: options.icon || null,
|
|
364
|
+
|
|
365
|
+
// Optional signing configuration
|
|
366
|
+
certificateThumbprint: options.certificateThumbprint || null,
|
|
367
|
+
certificateSubject: options.certificateSubject || null,
|
|
368
|
+
certificatePath: options.certificatePath || null,
|
|
369
|
+
certificatePassword: options.certificatePassword || null,
|
|
370
|
+
|
|
371
|
+
// Generated info
|
|
372
|
+
_generated: {
|
|
373
|
+
version: require("../package.json").version,
|
|
374
|
+
timestamp: new Date().toISOString(),
|
|
375
|
+
node: process.version,
|
|
376
|
+
platform: process.platform,
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
// Write config file
|
|
381
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2));
|
|
382
|
+
|
|
383
|
+
console.log(chalk.green(`✅ Configuration file created: ${configPath}`));
|
|
384
|
+
console.log(
|
|
385
|
+
chalk.blue(
|
|
386
|
+
"💡 Edit the configuration file and then run the packaging command",
|
|
387
|
+
),
|
|
388
|
+
);
|
|
389
|
+
|
|
390
|
+
return configPath;
|
|
391
|
+
} catch (error) {
|
|
392
|
+
console.error(
|
|
393
|
+
chalk.red(`❌ Config initialization failed: ${error.message}`),
|
|
394
|
+
);
|
|
395
|
+
throw error;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Export main functions
|
|
400
|
+
module.exports = {
|
|
401
|
+
createMsixPackage,
|
|
402
|
+
signMsixPackage,
|
|
403
|
+
listCertificates,
|
|
404
|
+
initConfig,
|
|
405
|
+
|
|
406
|
+
// For backward compatibility
|
|
407
|
+
findCodeSigningCertificates: listCertificates,
|
|
408
|
+
|
|
409
|
+
// Version info
|
|
410
|
+
version: require("../package.json").version,
|
|
411
|
+
|
|
412
|
+
// Constants for external use
|
|
413
|
+
CONSTANTS,
|
|
414
|
+
};
|