stacktape 3.5.6 → 3.5.8
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/bin/stacktape.js +206 -29
- package/package.json +1 -1
- package/plain.d.ts +12 -7
package/bin/stacktape.js
CHANGED
|
@@ -14,7 +14,11 @@ const {
|
|
|
14
14
|
accessSync,
|
|
15
15
|
constants,
|
|
16
16
|
unlinkSync,
|
|
17
|
-
readFileSync
|
|
17
|
+
readFileSync,
|
|
18
|
+
writeFileSync,
|
|
19
|
+
readdirSync,
|
|
20
|
+
rmSync,
|
|
21
|
+
statSync
|
|
18
22
|
} = require('node:fs');
|
|
19
23
|
const { get: httpsGet } = require('node:https');
|
|
20
24
|
const { platform, arch, homedir } = require('node:os');
|
|
@@ -33,6 +37,18 @@ const PLATFORM_MAP = {
|
|
|
33
37
|
'linux-x64-musl': { fileName: 'alpine.tar.gz', extract: extractTarGz }
|
|
34
38
|
};
|
|
35
39
|
|
|
40
|
+
const REQUIRED_HELPER_LAMBDA_PREFIXES = [
|
|
41
|
+
'stacktapeServiceLambda',
|
|
42
|
+
'cdnOriginRequestLambda',
|
|
43
|
+
'cdnOriginResponseLambda',
|
|
44
|
+
'batchJobTriggerLambda'
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const INSTALL_MARKER_FILE_NAME = '.stacktape-install.json';
|
|
48
|
+
const INSTALL_LOCK_DIR_SUFFIX = '.stacktape-install.lock';
|
|
49
|
+
const LOCK_WAIT_TIMEOUT_MS = 120000;
|
|
50
|
+
const STALE_LOCK_TIMEOUT_MS = 300000;
|
|
51
|
+
|
|
36
52
|
// ANSI color codes
|
|
37
53
|
const colors = {
|
|
38
54
|
reset: '\x1B[0m',
|
|
@@ -200,55 +216,207 @@ async function ensureBinary() {
|
|
|
200
216
|
|
|
201
217
|
const binaryName = platform() === 'win32' ? 'stacktape.exe' : 'stacktape';
|
|
202
218
|
|
|
219
|
+
const localCacheDir = join(__dirname, '..', 'bin');
|
|
220
|
+
|
|
203
221
|
let cacheDir;
|
|
204
222
|
try {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
cacheDir = localDir;
|
|
223
|
+
mkdirSync(localCacheDir, { recursive: true });
|
|
224
|
+
accessSync(localCacheDir, constants.W_OK);
|
|
225
|
+
cacheDir = localCacheDir;
|
|
209
226
|
} catch {
|
|
210
227
|
cacheDir = join(homedir(), '.stacktape', 'bin', version);
|
|
211
228
|
}
|
|
212
229
|
|
|
213
230
|
const binaryPath = join(cacheDir, binaryName);
|
|
231
|
+
const preserveLauncherScript = cacheDir === localCacheDir;
|
|
214
232
|
|
|
215
|
-
|
|
216
|
-
return binaryPath;
|
|
217
|
-
}
|
|
233
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
218
234
|
|
|
219
|
-
|
|
235
|
+
const lockDirPath = `${cacheDir}${INSTALL_LOCK_DIR_SUFFIX}`;
|
|
220
236
|
|
|
221
|
-
|
|
237
|
+
const isHelperLambdasCacheComplete = () => {
|
|
238
|
+
const helperLambdasDir = join(cacheDir, 'helper-lambdas');
|
|
239
|
+
if (!existsSync(helperLambdasDir)) {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
222
242
|
|
|
223
|
-
|
|
224
|
-
|
|
243
|
+
try {
|
|
244
|
+
const files = readdirSync(helperLambdasDir);
|
|
245
|
+
return REQUIRED_HELPER_LAMBDA_PREFIXES.every((prefix) =>
|
|
246
|
+
files.some((fileName) => fileName.startsWith(`${prefix}-`) && fileName.endsWith('.zip'))
|
|
247
|
+
);
|
|
248
|
+
} catch {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
};
|
|
225
252
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
253
|
+
const isCacheValid = () => {
|
|
254
|
+
if (!existsSync(binaryPath)) {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
229
257
|
|
|
230
|
-
|
|
231
|
-
|
|
258
|
+
if (!isHelperLambdasCacheComplete()) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
232
261
|
|
|
233
|
-
|
|
262
|
+
const markerPath = join(cacheDir, INSTALL_MARKER_FILE_NAME);
|
|
263
|
+
if (!existsSync(markerPath)) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
234
266
|
|
|
235
|
-
|
|
267
|
+
try {
|
|
268
|
+
const parsedMarker = JSON.parse(readFileSync(markerPath, 'utf8'));
|
|
269
|
+
return parsedMarker.version === version && parsedMarker.platformKey === platformKey;
|
|
270
|
+
} catch {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
};
|
|
236
274
|
|
|
237
|
-
|
|
238
|
-
|
|
275
|
+
const writeInstallMarker = () => {
|
|
276
|
+
const markerPath = join(cacheDir, INSTALL_MARKER_FILE_NAME);
|
|
277
|
+
writeFileSync(
|
|
278
|
+
markerPath,
|
|
279
|
+
JSON.stringify(
|
|
280
|
+
{
|
|
281
|
+
version,
|
|
282
|
+
platformKey,
|
|
283
|
+
helperLambdas: REQUIRED_HELPER_LAMBDA_PREFIXES,
|
|
284
|
+
installedAt: new Date().toISOString()
|
|
285
|
+
},
|
|
286
|
+
null,
|
|
287
|
+
2
|
|
288
|
+
)
|
|
289
|
+
);
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const cleanupExtractedCache = () => {
|
|
293
|
+
if (!existsSync(cacheDir)) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
const entries = readdirSync(cacheDir);
|
|
299
|
+
for (const entry of entries) {
|
|
300
|
+
if (preserveLauncherScript && entry === 'stacktape.js') {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
rmSync(join(cacheDir, entry), { recursive: true, force: true });
|
|
304
|
+
}
|
|
305
|
+
} catch {
|
|
306
|
+
// Ignore cleanup errors and try reinstall anyway
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
const acquireInstallLock = async () => {
|
|
311
|
+
const start = Date.now();
|
|
312
|
+
while (true) {
|
|
313
|
+
try {
|
|
314
|
+
mkdirSync(lockDirPath);
|
|
315
|
+
return;
|
|
316
|
+
} catch (error) {
|
|
317
|
+
if (error.code !== 'EEXIST') {
|
|
318
|
+
throw error;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
const lockStats = statSync(lockDirPath);
|
|
323
|
+
const lockAge = Date.now() - lockStats.mtimeMs;
|
|
324
|
+
if (lockAge > STALE_LOCK_TIMEOUT_MS) {
|
|
325
|
+
rmSync(lockDirPath, { recursive: true, force: true });
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
} catch {
|
|
329
|
+
// Lock directory disappeared between checks
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (Date.now() - start > LOCK_WAIT_TIMEOUT_MS) {
|
|
333
|
+
throw new Error('Timed out waiting for Stacktape binary installation lock');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
await sleep(200);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const releaseInstallLock = () => {
|
|
342
|
+
try {
|
|
343
|
+
rmSync(lockDirPath, { recursive: true, force: true });
|
|
344
|
+
} catch {
|
|
345
|
+
// Ignore release lock errors
|
|
239
346
|
}
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const installBinary = async () => {
|
|
350
|
+
console.info(`${colors.dim}Installing Stacktape ${version} for ${platformKey}...${colors.reset}`);
|
|
351
|
+
|
|
352
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
353
|
+
|
|
354
|
+
const downloadUrl = `https://github.com/${GITHUB_REPO}/releases/download/${version}/${platformInfo.fileName}`;
|
|
355
|
+
const archivePath = join(cacheDir, `.download-${Date.now()}-${platformInfo.fileName}`);
|
|
356
|
+
|
|
357
|
+
try {
|
|
358
|
+
console.info(`${colors.dim}Downloading from GitHub releases...${colors.reset}`);
|
|
359
|
+
await downloadFile(downloadUrl, archivePath);
|
|
360
|
+
|
|
361
|
+
console.info(`${colors.dim}Extracting...${colors.reset}`);
|
|
362
|
+
await platformInfo.extract(archivePath, cacheDir);
|
|
240
363
|
|
|
241
|
-
|
|
364
|
+
setExecutablePermissions(cacheDir);
|
|
242
365
|
|
|
366
|
+
unlinkSync(archivePath);
|
|
367
|
+
|
|
368
|
+
if (!existsSync(binaryPath)) {
|
|
369
|
+
throw new Error(`Binary not found after extraction: ${binaryPath}`);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (!isHelperLambdasCacheComplete()) {
|
|
373
|
+
throw new Error('Incomplete installation: helper lambdas were not extracted correctly');
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
writeInstallMarker();
|
|
377
|
+
printLogo();
|
|
378
|
+
|
|
379
|
+
return binaryPath;
|
|
380
|
+
} catch (error) {
|
|
381
|
+
try {
|
|
382
|
+
if (existsSync(archivePath)) {
|
|
383
|
+
unlinkSync(archivePath);
|
|
384
|
+
}
|
|
385
|
+
} catch {
|
|
386
|
+
// Ignore archive cleanup errors
|
|
387
|
+
}
|
|
388
|
+
throw error;
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
if (isCacheValid()) {
|
|
243
393
|
return binaryPath;
|
|
244
|
-
}
|
|
245
|
-
console.error(`
|
|
246
|
-
${colors.red}Error installing Stacktape:${colors.reset}
|
|
247
|
-
${error.message}
|
|
394
|
+
}
|
|
248
395
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
396
|
+
await acquireInstallLock();
|
|
397
|
+
|
|
398
|
+
try {
|
|
399
|
+
if (isCacheValid()) {
|
|
400
|
+
return binaryPath;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
let lastError;
|
|
404
|
+
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
405
|
+
try {
|
|
406
|
+
cleanupExtractedCache();
|
|
407
|
+
return await installBinary();
|
|
408
|
+
} catch (error) {
|
|
409
|
+
lastError = error;
|
|
410
|
+
cleanupExtractedCache();
|
|
411
|
+
if (attempt < 2) {
|
|
412
|
+
console.info(`\n${colors.dim}Retrying installation (${attempt + 1}/2)...${colors.reset}`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
throw lastError;
|
|
418
|
+
} finally {
|
|
419
|
+
releaseInstallLock();
|
|
252
420
|
}
|
|
253
421
|
}
|
|
254
422
|
|
|
@@ -269,6 +437,7 @@ function getGlobalBinaryPathIfVersionMatches() {
|
|
|
269
437
|
const globalDir = join(homedir(), '.stacktape', 'bin');
|
|
270
438
|
const globalBinaryPath = join(globalDir, binaryName);
|
|
271
439
|
const releaseDataPath = join(globalDir, 'release-data.json');
|
|
440
|
+
const globalHelperLambdasDir = join(globalDir, 'helper-lambdas');
|
|
272
441
|
|
|
273
442
|
if (!existsSync(globalBinaryPath) || !existsSync(releaseDataPath)) {
|
|
274
443
|
return null;
|
|
@@ -279,6 +448,14 @@ function getGlobalBinaryPathIfVersionMatches() {
|
|
|
279
448
|
if (version !== PACKAGE_VERSION) {
|
|
280
449
|
return null;
|
|
281
450
|
}
|
|
451
|
+
|
|
452
|
+
const files = readdirSync(globalHelperLambdasDir);
|
|
453
|
+
const hasAllHelperLambdas = REQUIRED_HELPER_LAMBDA_PREFIXES.every((prefix) =>
|
|
454
|
+
files.some((fileName) => fileName.startsWith(`${prefix}-`) && fileName.endsWith('.zip'))
|
|
455
|
+
);
|
|
456
|
+
if (!hasAllHelperLambdas) {
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
282
459
|
} catch {
|
|
283
460
|
return null;
|
|
284
461
|
}
|
package/package.json
CHANGED
package/plain.d.ts
CHANGED
|
@@ -6275,14 +6275,16 @@ export interface ApplicationLoadBalancerProps {
|
|
|
6275
6275
|
*/
|
|
6276
6276
|
interface?: "internal" | "internet";
|
|
6277
6277
|
/**
|
|
6278
|
-
* #### Custom domains.
|
|
6278
|
+
* #### Custom domains.
|
|
6279
6279
|
*
|
|
6280
6280
|
* ---
|
|
6281
6281
|
*
|
|
6282
|
-
*
|
|
6283
|
-
*
|
|
6282
|
+
* By default, Stacktape creates DNS records and TLS certificates for each domain.
|
|
6283
|
+
* If you manage DNS yourself, set `disableDnsRecordCreation` and provide `customCertificateArn`.
|
|
6284
|
+
*
|
|
6285
|
+
* Backward compatible format `string[]` is still supported.
|
|
6284
6286
|
*/
|
|
6285
|
-
customDomains?: string[];
|
|
6287
|
+
customDomains?: string[] | DomainConfiguration[];
|
|
6286
6288
|
/**
|
|
6287
6289
|
* #### Custom listeners (port + protocol). Defaults to HTTPS on 443 + HTTP on 80 (redirecting to HTTPS).
|
|
6288
6290
|
*/
|
|
@@ -6594,13 +6596,16 @@ export interface NetworkLoadBalancerProps {
|
|
|
6594
6596
|
*/
|
|
6595
6597
|
interface?: "internal" | "internet";
|
|
6596
6598
|
/**
|
|
6597
|
-
* #### Custom domains.
|
|
6599
|
+
* #### Custom domains.
|
|
6598
6600
|
*
|
|
6599
6601
|
* ---
|
|
6600
6602
|
*
|
|
6601
|
-
*
|
|
6603
|
+
* By default, Stacktape creates DNS records and TLS certificates for each domain.
|
|
6604
|
+
* If you manage DNS yourself, set `disableDnsRecordCreation` and provide `customCertificateArn`.
|
|
6605
|
+
*
|
|
6606
|
+
* Backward compatible format `string[]` is still supported.
|
|
6602
6607
|
*/
|
|
6603
|
-
customDomains?: string[];
|
|
6608
|
+
customDomains?: string[] | DomainConfiguration[];
|
|
6604
6609
|
/**
|
|
6605
6610
|
* #### Listeners define which ports and protocols (TCP/TLS) this load balancer accepts traffic on.
|
|
6606
6611
|
*/
|