@beads/bd 0.51.0 → 0.55.4
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/package.json +1 -1
- package/scripts/postinstall.js +47 -10
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -128,6 +128,30 @@ function sleep(ms) {
|
|
|
128
128
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
// Wait for a file to become accessible (Windows file lock workaround).
|
|
132
|
+
// After fs.createWriteStream closes, Windows may hold the file lock for a
|
|
133
|
+
// short period. This function polls until the file can be opened exclusively.
|
|
134
|
+
async function waitForFileAccess(filePath, timeoutMs = 30000) {
|
|
135
|
+
const intervalMs = 200;
|
|
136
|
+
const startTime = Date.now();
|
|
137
|
+
|
|
138
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
139
|
+
try {
|
|
140
|
+
const fd = fs.openSync(filePath, 'r');
|
|
141
|
+
fs.closeSync(fd);
|
|
142
|
+
return; // File is accessible
|
|
143
|
+
} catch (err) {
|
|
144
|
+
if (err.code === 'EBUSY' || err.code === 'EPERM') {
|
|
145
|
+
await sleep(intervalMs);
|
|
146
|
+
} else {
|
|
147
|
+
return; // Not a lock error — let extraction attempt handle it
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Timed out, but proceed anyway — extractZip has its own retry logic
|
|
152
|
+
console.warn(`Warning: file ${path.basename(filePath)} may still be locked after ${timeoutMs}ms, attempting extraction anyway...`);
|
|
153
|
+
}
|
|
154
|
+
|
|
131
155
|
// Extract zip file (for Windows) with retry logic
|
|
132
156
|
async function extractZip(zipPath, destDir, binaryName) {
|
|
133
157
|
console.log(`Extracting ${zipPath}...`);
|
|
@@ -139,9 +163,10 @@ async function extractZip(zipPath, destDir, binaryName) {
|
|
|
139
163
|
try {
|
|
140
164
|
// Use unzip command or powershell on Windows
|
|
141
165
|
if (os.platform() === 'win32') {
|
|
142
|
-
|
|
166
|
+
// Use stdio: 'pipe' to capture error output for file-lock detection
|
|
167
|
+
execSync(`powershell -command "Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force"`, { stdio: 'pipe' });
|
|
143
168
|
} else {
|
|
144
|
-
execSync(`unzip -o "${zipPath}" -d "${destDir}"`, { stdio: '
|
|
169
|
+
execSync(`unzip -o "${zipPath}" -d "${destDir}"`, { stdio: 'pipe' });
|
|
145
170
|
}
|
|
146
171
|
|
|
147
172
|
// The binary should now be in destDir
|
|
@@ -154,20 +179,26 @@ async function extractZip(zipPath, destDir, binaryName) {
|
|
|
154
179
|
console.log(`Binary extracted to: ${extractedBinary}`);
|
|
155
180
|
return; // Success
|
|
156
181
|
} catch (err) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
182
|
+
// Combine all available error output for reliable detection.
|
|
183
|
+
// With stdio: 'pipe', the PowerShell error text is in err.stderr,
|
|
184
|
+
// while err.message only contains "Command failed: ...".
|
|
185
|
+
const stderr = err.stderr ? err.stderr.toString() : '';
|
|
186
|
+
const errorText = `${err.message} ${stderr}`;
|
|
187
|
+
|
|
188
|
+
const isFileLockError =
|
|
189
|
+
errorText.includes('being used by another process') ||
|
|
190
|
+
errorText.includes('Access is denied') ||
|
|
191
|
+
errorText.includes('cannot access the file') ||
|
|
192
|
+
errorText.includes('EBUSY');
|
|
162
193
|
|
|
163
194
|
if (isFileLockError && attempt < maxRetries) {
|
|
164
195
|
const delayMs = baseDelayMs * Math.pow(2, attempt - 1);
|
|
165
|
-
console.log(`File
|
|
196
|
+
console.log(`File appears locked (attempt ${attempt}/${maxRetries}). Retrying in ${delayMs}ms...`);
|
|
166
197
|
await sleep(delayMs);
|
|
167
198
|
} else if (attempt === maxRetries) {
|
|
168
|
-
throw new Error(`Failed to extract archive after ${maxRetries} attempts: ${err.message}`);
|
|
199
|
+
throw new Error(`Failed to extract archive after ${maxRetries} attempts: ${err.message}\n${stderr}`);
|
|
169
200
|
} else {
|
|
170
|
-
throw new Error(`Failed to extract archive: ${err.message}`);
|
|
201
|
+
throw new Error(`Failed to extract archive: ${err.message}\n${stderr}`);
|
|
171
202
|
}
|
|
172
203
|
}
|
|
173
204
|
}
|
|
@@ -201,6 +232,12 @@ async function install() {
|
|
|
201
232
|
console.log(`Downloading bd binary...`);
|
|
202
233
|
await downloadFile(downloadUrl, archivePath);
|
|
203
234
|
|
|
235
|
+
// On Windows, wait for the OS to release the file lock before extracting.
|
|
236
|
+
// Windows may hold the file handle for a short time after close().
|
|
237
|
+
if (process.platform === 'win32') {
|
|
238
|
+
await waitForFileAccess(archivePath);
|
|
239
|
+
}
|
|
240
|
+
|
|
204
241
|
// Extract the archive based on platform
|
|
205
242
|
if (platformName === 'windows') {
|
|
206
243
|
await extractZip(archivePath, binDir, binaryName);
|