stacktape 3.5.7 → 3.6.0-beta.0

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.
Files changed (123) hide show
  1. package/.tsconfig.bun-build.json +1 -0
  2. package/ai-docs/cli-ref/aws-profile-create.md +22 -0
  3. package/ai-docs/cli-ref/aws-profile-delete.md +22 -0
  4. package/ai-docs/cli-ref/aws-profile-list.md +20 -0
  5. package/ai-docs/cli-ref/aws-profile-update.md +22 -0
  6. package/ai-docs/cli-ref/bastion-session.md +29 -0
  7. package/ai-docs/cli-ref/bastion-tunnel.md +30 -0
  8. package/ai-docs/cli-ref/bucket-sync.md +30 -0
  9. package/ai-docs/cli-ref/cf-module-update.md +26 -0
  10. package/ai-docs/cli-ref/cf-rollback.md +28 -0
  11. package/ai-docs/cli-ref/codebuild-deploy.md +34 -0
  12. package/ai-docs/cli-ref/compile-template.md +25 -0
  13. package/ai-docs/cli-ref/container-session.md +30 -0
  14. package/ai-docs/cli-ref/debug-alarms.md +28 -0
  15. package/ai-docs/cli-ref/debug-aws-sdk.md +33 -0
  16. package/ai-docs/cli-ref/debug-container-exec.md +36 -0
  17. package/ai-docs/cli-ref/debug-dynamodb.md +35 -0
  18. package/ai-docs/cli-ref/debug-logs.md +34 -0
  19. package/ai-docs/cli-ref/debug-metrics.md +33 -0
  20. package/ai-docs/cli-ref/debug-opensearch.md +35 -0
  21. package/ai-docs/cli-ref/debug-redis.md +36 -0
  22. package/ai-docs/cli-ref/debug-sql.md +35 -0
  23. package/ai-docs/cli-ref/defaults-configure.md +29 -0
  24. package/ai-docs/cli-ref/defaults-list.md +20 -0
  25. package/ai-docs/cli-ref/delete.md +24 -0
  26. package/ai-docs/cli-ref/deploy.md +25 -0
  27. package/ai-docs/cli-ref/deployment-script-run.md +28 -0
  28. package/ai-docs/cli-ref/dev-stop.md +26 -0
  29. package/ai-docs/cli-ref/dev.md +45 -0
  30. package/ai-docs/cli-ref/domain-add.md +26 -0
  31. package/ai-docs/cli-ref/help.md +18 -0
  32. package/ai-docs/cli-ref/info-operations.md +22 -0
  33. package/ai-docs/cli-ref/info-stack.md +30 -0
  34. package/ai-docs/cli-ref/info-stacks.md +26 -0
  35. package/ai-docs/cli-ref/info-whoami.md +22 -0
  36. package/ai-docs/cli-ref/init.md +30 -0
  37. package/ai-docs/cli-ref/login.md +20 -0
  38. package/ai-docs/cli-ref/logout.md +18 -0
  39. package/ai-docs/cli-ref/mcp-add.md +22 -0
  40. package/ai-docs/cli-ref/mcp.md +20 -0
  41. package/ai-docs/cli-ref/org-create.md +24 -0
  42. package/ai-docs/cli-ref/org-delete.md +24 -0
  43. package/ai-docs/cli-ref/org-list.md +22 -0
  44. package/ai-docs/cli-ref/package-workloads.md +25 -0
  45. package/ai-docs/cli-ref/param-get.md +26 -0
  46. package/ai-docs/cli-ref/preview-changes.md +23 -0
  47. package/ai-docs/cli-ref/project-create.md +22 -0
  48. package/ai-docs/cli-ref/projects-list.md +22 -0
  49. package/ai-docs/cli-ref/rollback.md +28 -0
  50. package/ai-docs/cli-ref/script-run.md +29 -0
  51. package/ai-docs/cli-ref/secret-create.md +28 -0
  52. package/ai-docs/cli-ref/secret-delete.md +26 -0
  53. package/ai-docs/cli-ref/secret-get.md +26 -0
  54. package/ai-docs/cli-ref/upgrade.md +20 -0
  55. package/ai-docs/cli-ref/version.md +18 -0
  56. package/ai-docs/concept/connecting-resources.md +369 -0
  57. package/ai-docs/concept/directives.md +371 -0
  58. package/ai-docs/concept/extending-cloudformation.md +315 -0
  59. package/ai-docs/concept/overrides-and-transforms.md +352 -0
  60. package/ai-docs/concept/stages-and-environments.md +347 -0
  61. package/ai-docs/concept/typescript-config.md +447 -0
  62. package/ai-docs/concept/yaml-config.md +338 -0
  63. package/ai-docs/config-ref/_root.md +142 -0
  64. package/ai-docs/config-ref/application-load-balancer.md +1109 -0
  65. package/ai-docs/config-ref/astro-web.md +115 -0
  66. package/ai-docs/config-ref/aws-cdk-construct.md +68 -0
  67. package/ai-docs/config-ref/bastion.md +93 -0
  68. package/ai-docs/config-ref/batch-job.md +179 -0
  69. package/ai-docs/config-ref/bucket.md +348 -0
  70. package/ai-docs/config-ref/cdn.md +496 -0
  71. package/ai-docs/config-ref/custom-resource.md +80 -0
  72. package/ai-docs/config-ref/deployment-script.md +79 -0
  73. package/ai-docs/config-ref/dynamo-db-table.md +202 -0
  74. package/ai-docs/config-ref/edge-lambda-function.md +87 -0
  75. package/ai-docs/config-ref/efs-filesystem.md +72 -0
  76. package/ai-docs/config-ref/event-bus.md +63 -0
  77. package/ai-docs/config-ref/function.md +409 -0
  78. package/ai-docs/config-ref/hosting-bucket.md +171 -0
  79. package/ai-docs/config-ref/http-api-gateway.md +149 -0
  80. package/ai-docs/config-ref/http-endpoint.md +92 -0
  81. package/ai-docs/config-ref/kinesis-stream.md +97 -0
  82. package/ai-docs/config-ref/mongo-db-atlas-cluster.md +254 -0
  83. package/ai-docs/config-ref/multi-container-workload.md +399 -0
  84. package/ai-docs/config-ref/network-load-balancer.md +118 -0
  85. package/ai-docs/config-ref/nextjs-web.md +147 -0
  86. package/ai-docs/config-ref/nuxt-web.md +81 -0
  87. package/ai-docs/config-ref/open-search.md +206 -0
  88. package/ai-docs/config-ref/private-service.md +75 -0
  89. package/ai-docs/config-ref/redis-cluster.md +223 -0
  90. package/ai-docs/config-ref/relational-database.md +525 -0
  91. package/ai-docs/config-ref/remix-web.md +74 -0
  92. package/ai-docs/config-ref/sns-topic.md +69 -0
  93. package/ai-docs/config-ref/solidstart-web.md +75 -0
  94. package/ai-docs/config-ref/sqs-queue-not-empty.md +405 -0
  95. package/ai-docs/config-ref/sqs-queue.md +232 -0
  96. package/ai-docs/config-ref/state-machine.md +235 -0
  97. package/ai-docs/config-ref/sveltekit-web.md +81 -0
  98. package/ai-docs/config-ref/tanstack-web.md +75 -0
  99. package/ai-docs/config-ref/upstash-redis.md +59 -0
  100. package/ai-docs/config-ref/user-auth-pool.md +876 -0
  101. package/ai-docs/config-ref/web-app-firewall.md +212 -0
  102. package/ai-docs/config-ref/web-service.md +178 -0
  103. package/ai-docs/config-ref/worker-service.md +41 -0
  104. package/ai-docs/getting-started/console.md +232 -0
  105. package/ai-docs/getting-started/deployment.md +434 -0
  106. package/ai-docs/getting-started/dev-mode.md +118 -0
  107. package/ai-docs/getting-started/how-it-works.md +119 -0
  108. package/ai-docs/getting-started/intro.md +157 -0
  109. package/ai-docs/getting-started/using-with-ai.md +228 -0
  110. package/ai-docs/getting-started/workflow.md +197 -0
  111. package/ai-docs/index.json +1514 -0
  112. package/ai-docs/recipe/background-jobs.md +183 -0
  113. package/ai-docs/recipe/database-migrations.md +240 -0
  114. package/ai-docs/recipe/graphql-api.md +211 -0
  115. package/ai-docs/recipe/monorepo-setup.md +183 -0
  116. package/ai-docs/recipe/nextjs-full-stack.md +188 -0
  117. package/ai-docs/recipe/rest-api-with-database.md +156 -0
  118. package/ai-docs/recipe/scheduled-tasks.md +186 -0
  119. package/ai-docs/recipe/static-website.md +241 -0
  120. package/ai-docs/troubleshooting/cloudformation-stack-states.md +189 -0
  121. package/bin/stacktape.js +206 -41
  122. package/package.json +1 -1
  123. package/plain.d.ts +309 -54
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,68 +216,208 @@ 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
- const localDir = join(__dirname, '..', 'bin');
206
- mkdirSync(localDir, { recursive: true });
207
- accessSync(localDir, constants.W_OK);
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
- if (existsSync(binaryPath)) {
216
- return binaryPath;
217
- }
233
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
218
234
 
219
- console.info(`${colors.dim}Installing Stacktape ${version} for ${platformKey}...${colors.reset}`);
235
+ const lockDirPath = `${cacheDir}${INSTALL_LOCK_DIR_SUFFIX}`;
220
236
 
221
- mkdirSync(cacheDir, { recursive: true });
237
+ const isHelperLambdasCacheComplete = () => {
238
+ const helperLambdasDir = join(cacheDir, 'helper-lambdas');
239
+ if (!existsSync(helperLambdasDir)) {
240
+ return false;
241
+ }
222
242
 
223
- const downloadUrl = `https://github.com/${GITHUB_REPO}/releases/download/${version}/${platformInfo.fileName}`;
224
- const archivePath = join(cacheDir, platformInfo.fileName);
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
- try {
227
- console.info(`${colors.dim}Downloading from GitHub releases...${colors.reset}`);
228
- await downloadFile(downloadUrl, archivePath);
253
+ const isCacheValid = () => {
254
+ if (!existsSync(binaryPath)) {
255
+ return false;
256
+ }
229
257
 
230
- console.info(`${colors.dim}Extracting...${colors.reset}`);
231
- await platformInfo.extract(archivePath, cacheDir);
258
+ if (!isHelperLambdasCacheComplete()) {
259
+ return false;
260
+ }
232
261
 
233
- setExecutablePermissions(cacheDir);
262
+ const markerPath = join(cacheDir, INSTALL_MARKER_FILE_NAME);
263
+ if (!existsSync(markerPath)) {
264
+ return false;
265
+ }
234
266
 
235
- unlinkSync(archivePath);
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
- if (!existsSync(binaryPath)) {
238
- throw new Error(`Binary not found after extraction: ${binaryPath}`);
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;
239
295
  }
240
296
 
241
- printLogo();
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
+ };
242
309
 
243
- return binaryPath;
244
- } catch (error) {
245
- console.error(`
246
- ${colors.red}Error installing Stacktape:${colors.reset}
247
- ${error.message}
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
+ }
248
331
 
249
- You can also install Stacktape directly using:
250
- ${getManualInstallCommand(platformKey)}`);
251
- process.exit(1);
252
- }
253
- }
332
+ if (Date.now() - start > LOCK_WAIT_TIMEOUT_MS) {
333
+ throw new Error('Timed out waiting for Stacktape binary installation lock');
334
+ }
254
335
 
255
- function getManualInstallCommand(platformKey) {
256
- const commands = {
257
- 'win32-x64': 'iwr https://installs.stacktape.com/windows.ps1 -useb | iex',
258
- 'linux-x64': 'curl -L https://installs.stacktape.com/linux.sh | sh',
259
- 'linux-arm64': 'curl -L https://installs.stacktape.com/linux-arm.sh | sh',
260
- 'linux-x64-musl': 'curl -L https://installs.stacktape.com/alpine.sh | sh',
261
- 'darwin-x64': 'curl -L https://installs.stacktape.com/macos.sh | sh',
262
- 'darwin-arm64': 'curl -L https://installs.stacktape.com/macos-arm.sh | sh'
336
+ await sleep(200);
337
+ }
338
+ }
263
339
  };
264
- return commands[platformKey] || 'See https://docs.stacktape.com for installation instructions';
340
+
341
+ const releaseInstallLock = () => {
342
+ try {
343
+ rmSync(lockDirPath, { recursive: true, force: true });
344
+ } catch {
345
+ // Ignore release lock errors
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);
363
+
364
+ setExecutablePermissions(cacheDir);
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()) {
393
+ return binaryPath;
394
+ }
395
+
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();
420
+ }
265
421
  }
266
422
 
267
423
  function getGlobalBinaryPathIfVersionMatches() {
@@ -269,6 +425,7 @@ function getGlobalBinaryPathIfVersionMatches() {
269
425
  const globalDir = join(homedir(), '.stacktape', 'bin');
270
426
  const globalBinaryPath = join(globalDir, binaryName);
271
427
  const releaseDataPath = join(globalDir, 'release-data.json');
428
+ const globalHelperLambdasDir = join(globalDir, 'helper-lambdas');
272
429
 
273
430
  if (!existsSync(globalBinaryPath) || !existsSync(releaseDataPath)) {
274
431
  return null;
@@ -279,6 +436,14 @@ function getGlobalBinaryPathIfVersionMatches() {
279
436
  if (version !== PACKAGE_VERSION) {
280
437
  return null;
281
438
  }
439
+
440
+ const files = readdirSync(globalHelperLambdasDir);
441
+ const hasAllHelperLambdas = REQUIRED_HELPER_LAMBDA_PREFIXES.every((prefix) =>
442
+ files.some((fileName) => fileName.startsWith(`${prefix}-`) && fileName.endsWith('.zip'))
443
+ );
444
+ if (!hasAllHelperLambdas) {
445
+ return null;
446
+ }
282
447
  } catch {
283
448
  return null;
284
449
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stacktape",
3
- "version": "3.5.7",
3
+ "version": "3.6.0-beta.0",
4
4
  "description": "PaaS 2.0 that deploys to your own AWS account.",
5
5
  "author": "Stacktape team <support@stacktape.com>",
6
6
  "license": "MIT",