@stacksjs/ts-cloud 0.3.1 → 0.4.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.
package/dist/index.js CHANGED
@@ -39,17 +39,22 @@ import { cpSync, mkdtempSync, rmSync, writeFileSync as writeFileSync32 } from "n
39
39
  import { tmpdir } from "node:os";
40
40
  import { dirname as dirname22, isAbsolute as isAbsolute3, join as join62, resolve as resolve13 } from "node:path";
41
41
  import { fileURLToPath } from "node:url";
42
- import { readFileSync as readFileSync4 } from "node:fs";
43
- import { dirname as dirname32, join as join72 } from "node:path";
44
- import { fileURLToPath as fileURLToPath2 } from "node:url";
45
42
  import { execFileSync } from "node:child_process";
46
- import { existsSync as existsSync52, mkdtempSync as mkdtempSync2, readdirSync as readdirSync52, readFileSync as readFileSync5, rmSync as rmSync2, statSync as statSync32, writeFileSync as writeFileSync42 } from "node:fs";
43
+ import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync4, rmSync as rmSync2 } from "node:fs";
47
44
  import { tmpdir as tmpdir2 } from "node:os";
48
- import { join as join8, relative as relative22 } from "node:path";
45
+ import { dirname as dirname32, join as join72 } from "node:path";
46
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
47
+ import { readFileSync as readFileSync5 } from "node:fs";
48
+ import { dirname as dirname42, join as join8 } from "node:path";
49
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
50
+ import { execFileSync as execFileSync2 } from "node:child_process";
51
+ import { existsSync as existsSync52, mkdtempSync as mkdtempSync3, readdirSync as readdirSync52, readFileSync as readFileSync6, rmSync as rmSync3, statSync as statSync32, writeFileSync as writeFileSync52 } from "node:fs";
52
+ import { tmpdir as tmpdir3 } from "node:os";
53
+ import { join as join9, relative as relative22 } from "node:path";
49
54
  import { execSync as execSync2 } from "node:child_process";
50
55
  import { createHash as createHash5 } from "node:crypto";
51
- import { readdirSync as readdirSync62, readFileSync as readFileSync6, statSync as statSync4 } from "node:fs";
52
- import { join as join9, relative as relative32, resolve as resolve22 } from "node:path";
56
+ import { readdirSync as readdirSync62, readFileSync as readFileSync7, statSync as statSync4 } from "node:fs";
57
+ import { join as join10, relative as relative32, resolve as resolve22 } from "node:path";
53
58
  function __exportSetter3(name, newValue) {
54
59
  this[name] = __returnValue3.bind(null, newValue);
55
60
  }
@@ -25711,7 +25716,7 @@ async function packageServerlessApp(opts) {
25711
25716
  opts.onStep?.("bundling application");
25712
25717
  const result = await Bun.build({
25713
25718
  entrypoints: [bootstrapPath],
25714
- target: "node",
25719
+ target: app.kind === "bun" ? "bun" : "node",
25715
25720
  format: "esm",
25716
25721
  minify: false,
25717
25722
  sourcemap: "none"
@@ -25742,6 +25747,54 @@ ${logs}`);
25742
25747
  rmSync(stage, { recursive: true, force: true });
25743
25748
  }
25744
25749
  }
25750
+ function normalizeNodeVersion(v) {
25751
+ if (!v)
25752
+ return "22";
25753
+ const m = String(v).match(/(\d+)/);
25754
+ return m ? m[1] : "22";
25755
+ }
25756
+ function resolveServerlessRuntime(app) {
25757
+ const kind = app.kind ?? "node";
25758
+ if (kind === "php") {
25759
+ return {
25760
+ lambdaRuntime: "provided.al2023",
25761
+ kind: "php",
25762
+ version: app.phpVersion ?? "8.3",
25763
+ usesLayer: true,
25764
+ layerKind: "php",
25765
+ layerEnvVar: "TSCLOUD_PHP_LAYER_ARN"
25766
+ };
25767
+ }
25768
+ if (kind === "bun") {
25769
+ return {
25770
+ lambdaRuntime: "provided.al2023",
25771
+ kind: "bun",
25772
+ version: app.runtimeVersion ?? "latest",
25773
+ usesLayer: true,
25774
+ layerKind: "bun",
25775
+ layerEnvVar: "TSCLOUD_BUN_LAYER_ARN"
25776
+ };
25777
+ }
25778
+ const version2 = normalizeNodeVersion(app.runtimeVersion);
25779
+ const explicitProvided = app.runtime === "provided.al2023";
25780
+ const managed = !explicitProvided && MANAGED_NODE_VERSIONS.includes(version2);
25781
+ if (managed) {
25782
+ return {
25783
+ lambdaRuntime: app.runtime && app.runtime.startsWith("nodejs") ? app.runtime : `nodejs${version2}.x`,
25784
+ kind: "node",
25785
+ version: version2,
25786
+ usesLayer: false
25787
+ };
25788
+ }
25789
+ return {
25790
+ lambdaRuntime: "provided.al2023",
25791
+ kind: "node",
25792
+ version: version2,
25793
+ usesLayer: true,
25794
+ layerKind: "node",
25795
+ layerEnvVar: "TSCLOUD_NODE_LAYER_ARN"
25796
+ };
25797
+ }
25745
25798
  function resolveQueueNames(app, slug, env) {
25746
25799
  if (app.queues === false)
25747
25800
  return [];
@@ -25755,7 +25808,7 @@ function resolveQueueNames(app, slug, env) {
25755
25808
  function composeServerlessAppTemplate(opts) {
25756
25809
  const { app, environment, handlers } = opts;
25757
25810
  const slug = opts.config.project.slug;
25758
- const runtime = app.runtime ?? "nodejs20.x";
25811
+ const runtime = resolveServerlessRuntime(app).lambdaRuntime;
25759
25812
  const architecture = app.architecture ?? "x86_64";
25760
25813
  const region = opts.config.project.region;
25761
25814
  const functionNames = {
@@ -26360,6 +26413,79 @@ COPY app/ \${LAMBDA_TASK_ROOT}/
26360
26413
  CMD [ "index.http" ]
26361
26414
  `;
26362
26415
  }
26416
+ function assetsDir() {
26417
+ return dirname32(fileURLToPath2(import.meta.url));
26418
+ }
26419
+ function sharedRuntimeLoop() {
26420
+ return readFileSync4(join72(assetsDir(), "runtime.mjs"), "utf-8");
26421
+ }
26422
+ function bootstrap(kind) {
26423
+ return readFileSync4(join72(assetsDir(), `${kind}-bootstrap`), "utf-8");
26424
+ }
26425
+ function buildNodeRuntimeLayerZip(options) {
26426
+ const architecture = options.architecture ?? "x86_64";
26427
+ const nodeArch = architecture === "arm64" ? "arm64" : "x64";
26428
+ const version2 = options.version.replace(/^v/, "");
26429
+ const step = options.onStep ?? (() => {});
26430
+ const stage = mkdtempSync2(join72(tmpdir2(), "tscloud-node-layer-"));
26431
+ try {
26432
+ const exact = version2.includes(".") ? version2 : resolveLatestNode(version2);
26433
+ const dir = `node-v${exact}-linux-${nodeArch}`;
26434
+ const url = `https://nodejs.org/dist/v${exact}/${dir}.tar.xz`;
26435
+ step(`downloading Node ${exact} (${architecture})`);
26436
+ const tar = join72(stage, "node.tar.xz");
26437
+ fetchToFile(url, tar);
26438
+ step("extracting node binary");
26439
+ execFileSync("tar", ["-xf", tar, "-C", stage, `${dir}/bin/node`], { stdio: "inherit" });
26440
+ const nodeBin = readFileSync4(join72(stage, dir, "bin", "node"));
26441
+ step("packaging layer");
26442
+ const entries = [
26443
+ { name: "bootstrap", data: bootstrap("node"), mode: 493 },
26444
+ { name: "runtime.mjs", data: sharedRuntimeLoop(), mode: 420 },
26445
+ { name: "bin/node", data: nodeBin, mode: 493 }
26446
+ ];
26447
+ return { zip: createZip(entries), architecture, version: exact, fileCount: entries.length };
26448
+ } finally {
26449
+ rmSync2(stage, { recursive: true, force: true });
26450
+ }
26451
+ }
26452
+ function buildBunRuntimeLayerZip(options) {
26453
+ const architecture = options.architecture ?? "x86_64";
26454
+ const bunArch = architecture === "arm64" ? "bun-linux-aarch64" : "bun-linux-x64";
26455
+ const version2 = options.version === "latest" ? "" : options.version.replace(/^v/, "");
26456
+ const step = options.onStep ?? (() => {});
26457
+ const stage = mkdtempSync2(join72(tmpdir2(), "tscloud-bun-layer-"));
26458
+ try {
26459
+ const tag = version2 ? `bun-v${version2}` : "latest";
26460
+ const url = `https://github.com/oven-sh/bun/releases/download/${tag}/${bunArch}.zip`;
26461
+ step(`downloading Bun ${version2 || "latest"} (${architecture})`);
26462
+ const zipPath = join72(stage, "bun.zip");
26463
+ fetchToFile(url, zipPath);
26464
+ step("extracting bun binary");
26465
+ execFileSync("unzip", ["-o", "-q", zipPath, "-d", stage], { stdio: "inherit" });
26466
+ const bunBin = readFileSync4(join72(stage, bunArch, "bun"));
26467
+ step("packaging layer");
26468
+ const entries = [
26469
+ { name: "bootstrap", data: bootstrap("bun"), mode: 493 },
26470
+ { name: "runtime.mjs", data: sharedRuntimeLoop(), mode: 420 },
26471
+ { name: "bin/bun", data: bunBin, mode: 493 }
26472
+ ];
26473
+ return { zip: createZip(entries), architecture, version: version2 || "latest", fileCount: entries.length };
26474
+ } finally {
26475
+ rmSync2(stage, { recursive: true, force: true });
26476
+ }
26477
+ }
26478
+ function fetchToFile(url, dest) {
26479
+ execFileSync("curl", ["-fsSL", "-o", dest, url], { stdio: ["ignore", "ignore", "inherit"] });
26480
+ }
26481
+ function resolveLatestNode(major) {
26482
+ const indexJson = execFileSync("curl", ["-fsSL", "https://nodejs.org/dist/index.json"], { encoding: "utf-8" });
26483
+ const releases = JSON.parse(indexJson);
26484
+ const match = releases.find((r) => r.version.startsWith(`v${major}.`));
26485
+ if (!match)
26486
+ throw new Error(`No Node release found for major version ${major}`);
26487
+ return match.version.replace(/^v/, "");
26488
+ }
26363
26489
  function resolveApp(mod) {
26364
26490
  const m = mod;
26365
26491
  const def = m?.default ?? m;
@@ -26508,12 +26634,12 @@ php_admin_flag[log_errors] = on
26508
26634
  clear_env = no
26509
26635
  `;
26510
26636
  }
26511
- function assetsDir() {
26512
- return join72(dirname32(fileURLToPath2(import.meta.url)), "runtime-assets");
26637
+ function assetsDir2() {
26638
+ return join8(dirname42(fileURLToPath3(import.meta.url)), "runtime-assets");
26513
26639
  }
26514
26640
  function phpRuntimeLayerAssets() {
26515
- const dir = assetsDir();
26516
- const read = (f) => readFileSync4(join72(dir, f), "utf-8");
26641
+ const dir = assetsDir2();
26642
+ const read = (f) => readFileSync5(join8(dir, f), "utf-8");
26517
26643
  return [
26518
26644
  { path: "bootstrap", contents: read("bootstrap"), mode: 493 },
26519
26645
  { path: "tscloud/runtime.php", contents: read("runtime.php"), mode: 420 },
@@ -26543,7 +26669,7 @@ function laravelServerlessEnvDefaults(opts = {}) {
26543
26669
  }
26544
26670
  function* walk(dir) {
26545
26671
  for (const entry of readdirSync52(dir)) {
26546
- const full = join8(dir, entry);
26672
+ const full = join9(dir, entry);
26547
26673
  if (statSync32(full).isDirectory())
26548
26674
  yield* walk(full);
26549
26675
  else
@@ -26554,19 +26680,19 @@ function buildPhpRuntimeLayerZip(options = {}) {
26554
26680
  const architecture = options.architecture ?? "x86_64";
26555
26681
  const platform = options.platform ?? (architecture === "arm64" ? "linux/arm64" : "linux/amd64");
26556
26682
  const step = options.onStep ?? (() => {});
26557
- const stage = mkdtempSync2(join8(tmpdir2(), "tscloud-php-layer-"));
26683
+ const stage = mkdtempSync3(join9(tmpdir3(), "tscloud-php-layer-"));
26558
26684
  const imageTag = "tscloud-php-layer:build";
26559
26685
  try {
26560
- writeFileSync42(join8(stage, "Dockerfile"), generatePhpLayerDockerfile(options));
26686
+ writeFileSync52(join9(stage, "Dockerfile"), generatePhpLayerDockerfile(options));
26561
26687
  step("Building PHP runtime image (docker)");
26562
- execFileSync("docker", ["build", "--platform", platform, "-t", imageTag, stage], { stdio: "inherit" });
26688
+ execFileSync2("docker", ["build", "--platform", platform, "-t", imageTag, stage], { stdio: "inherit" });
26563
26689
  step("Extracting /opt from image");
26564
- const cid = execFileSync("docker", ["create", "--platform", platform, imageTag], { encoding: "utf-8" }).trim();
26565
- const optDir = join8(stage, "opt");
26690
+ const cid = execFileSync2("docker", ["create", "--platform", platform, imageTag], { encoding: "utf-8" }).trim();
26691
+ const optDir = join9(stage, "opt");
26566
26692
  try {
26567
- execFileSync("docker", ["cp", `${cid}:/opt/.`, optDir], { stdio: "inherit" });
26693
+ execFileSync2("docker", ["cp", `${cid}:/opt/.`, optDir], { stdio: "inherit" });
26568
26694
  } finally {
26569
- execFileSync("docker", ["rm", cid], { stdio: "ignore" });
26695
+ execFileSync2("docker", ["rm", cid], { stdio: "ignore" });
26570
26696
  }
26571
26697
  if (!existsSync52(optDir))
26572
26698
  throw new Error("layer build produced no /opt directory");
@@ -26574,7 +26700,7 @@ function buildPhpRuntimeLayerZip(options = {}) {
26574
26700
  for (const file of walk(optDir)) {
26575
26701
  const rel = relative22(optDir, file).replace(/\\/g, "/");
26576
26702
  const mode = statSync32(file).mode & 73 ? 493 : 420;
26577
- entries.push({ name: rel, data: readFileSync5(file), mode });
26703
+ entries.push({ name: rel, data: readFileSync6(file), mode });
26578
26704
  }
26579
26705
  step("Injecting runtime assets");
26580
26706
  const assetPaths = new Set(phpRuntimeLayerAssets().map((a) => a.path));
@@ -26586,7 +26712,7 @@ function buildPhpRuntimeLayerZip(options = {}) {
26586
26712
  const zip2 = createZip(filtered);
26587
26713
  return { zip: zip2, architecture, fileCount: filtered.length };
26588
26714
  } finally {
26589
- rmSync2(stage, { recursive: true, force: true });
26715
+ rmSync3(stage, { recursive: true, force: true });
26590
26716
  }
26591
26717
  }
26592
26718
  function isExcluded(rel, excludes) {
@@ -26594,7 +26720,7 @@ function isExcluded(rel, excludes) {
26594
26720
  }
26595
26721
  function* walk2(dir, root, excludes) {
26596
26722
  for (const entry of readdirSync62(dir)) {
26597
- const full = join9(dir, entry);
26723
+ const full = join10(dir, entry);
26598
26724
  const rel = relative32(root, full).replace(/\\/g, "/");
26599
26725
  if (isExcluded(rel, excludes))
26600
26726
  continue;
@@ -26621,7 +26747,7 @@ function collectPhpAppEntries(projectRoot, exclude = []) {
26621
26747
  for (const file of walk2(root, root, excludes)) {
26622
26748
  const rel = relative32(root, file).replace(/\\/g, "/");
26623
26749
  const executable = (statSync4(file).mode & 73) !== 0;
26624
- entries.push({ name: rel, data: readFileSync6(file), mode: executable ? 493 : 420 });
26750
+ entries.push({ name: rel, data: readFileSync7(file), mode: executable ? 493 : 420 });
26625
26751
  }
26626
26752
  return entries;
26627
26753
  }
@@ -28068,7 +28194,7 @@ catch (error) {
28068
28194
 
28069
28195
  return { statusCode: 200, body: 'OK' };
28070
28196
  };
28071
- `, inbound_default, exports_advanced2, IVRBuilder, exports_advanced3, MmsSupport, SmsChatbot, fifoQueueManager, dlqMonitoringManager, batchProcessingManager, queueManagementManager, CRC_TABLE, PHP_LAYER_EXTENSIONS, TEXT_CONTENT, LARAVEL_SERVERLESS_BUILD_STEPS, PHP_DEFAULT_EXCLUDES, staticSiteManager, storageAdvancedManager, healthCheckManager, networkSecurityManager, resourceManagementManager;
28197
+ `, inbound_default, exports_advanced2, IVRBuilder, exports_advanced3, MmsSupport, SmsChatbot, fifoQueueManager, dlqMonitoringManager, batchProcessingManager, queueManagementManager, CRC_TABLE, MANAGED_NODE_VERSIONS, PHP_LAYER_EXTENSIONS, TEXT_CONTENT, LARAVEL_SERVERLESS_BUILD_STEPS, PHP_DEFAULT_EXCLUDES, staticSiteManager, storageAdvancedManager, healthCheckManager, networkSecurityManager, resourceManagementManager;
28072
28198
  var init_dist2 = __esm(async () => {
28073
28199
  __defProp3 = Object.defineProperty;
28074
28200
  __require3 = /* @__PURE__ */ createRequire2(import.meta.url);
@@ -41791,7 +41917,8 @@ Details:`;
41791
41917
  type: "object",
41792
41918
  description: "Serverless application manifest (Laravel-Vapor-equivalent). Defining this opts the environment into the serverless app deploy pipeline (http/queue/cli Lambda functions, assets, build/deploy hooks).",
41793
41919
  properties: {
41794
- runtime: { type: "string", description: "Lambda runtime (e.g. nodejs20.x, provided.al2023)" },
41920
+ runtime: { type: "string", description: "Lambda runtime override (usually derived from kind + runtimeVersion)" },
41921
+ runtimeVersion: { type: "string", description: "Node major (18/20/22 managed, 24+ custom layer) or Bun release for node/bun apps" },
41795
41922
  kind: { type: "string", enum: ["node", "bun", "php"], description: "Application kind (drives packaging + runtime)" },
41796
41923
  entry: { type: "string", description: "Entry file exporting the request handler" },
41797
41924
  memory: { type: "number", description: "HTTP function memory in MB" },
@@ -45604,6 +45731,7 @@ async function sendFallbackSms(to, message) {
45604
45731
  }
45605
45732
  return table2;
45606
45733
  })();
45734
+ MANAGED_NODE_VERSIONS = ["18", "20", "22"];
45607
45735
  PHP_LAYER_EXTENSIONS = [
45608
45736
  "cli",
45609
45737
  "fpm",
@@ -46829,9 +46957,9 @@ __export(exports_client, {
46829
46957
  AWSClient: () => AWSClient
46830
46958
  });
46831
46959
  import * as crypto2 from "node:crypto";
46832
- import { existsSync as existsSync14, readFileSync as readFileSync7 } from "node:fs";
46960
+ import { existsSync as existsSync14, readFileSync as readFileSync8 } from "node:fs";
46833
46961
  import { homedir as homedir6 } from "node:os";
46834
- import { join as join10 } from "node:path";
46962
+ import { join as join11 } from "node:path";
46835
46963
  function resolveS3Endpoint(options) {
46836
46964
  const base = options.endpoint || `s3.${options.region}.amazonaws.com`;
46837
46965
  if (!options.bucket) {
@@ -46889,12 +47017,12 @@ class AWSClient {
46889
47017
  }
46890
47018
  loadCredentialsFromFile() {
46891
47019
  const profile = process.env.AWS_PROFILE || "default";
46892
- const credentialsPath = process.env.AWS_SHARED_CREDENTIALS_FILE || join10(homedir6(), ".aws", "credentials");
47020
+ const credentialsPath = process.env.AWS_SHARED_CREDENTIALS_FILE || join11(homedir6(), ".aws", "credentials");
46893
47021
  if (!existsSync14(credentialsPath)) {
46894
47022
  return null;
46895
47023
  }
46896
47024
  try {
46897
- const content = readFileSync7(credentialsPath, "utf-8");
47025
+ const content = readFileSync8(credentialsPath, "utf-8");
46898
47026
  const credentials = this.parseCredentialsFile(content, profile);
46899
47027
  if (credentials.accessKeyId && credentials.secretAccessKey) {
46900
47028
  return credentials;
@@ -47388,7 +47516,7 @@ function detectCredentialSource() {
47388
47516
  };
47389
47517
  }
47390
47518
  const profile = process.env.AWS_PROFILE || "default";
47391
- const credentialsPath = process.env.AWS_SHARED_CREDENTIALS_FILE || join10(homedir6(), ".aws", "credentials");
47519
+ const credentialsPath = process.env.AWS_SHARED_CREDENTIALS_FILE || join11(homedir6(), ".aws", "credentials");
47392
47520
  if (existsSync14(credentialsPath)) {
47393
47521
  return {
47394
47522
  source: "file",
@@ -47430,13 +47558,13 @@ function resolveCredentials2(profile) {
47430
47558
  return loadProfileFromFile(process.env.AWS_PROFILE || "default") ?? { accessKeyId: "", secretAccessKey: "" };
47431
47559
  }
47432
47560
  function loadProfileFromFile(profile) {
47433
- const { existsSync: existsSync15, readFileSync: readFileSync8 } = __require("node:fs");
47561
+ const { existsSync: existsSync15, readFileSync: readFileSync9 } = __require("node:fs");
47434
47562
  const { homedir: homedir5 } = __require("node:os");
47435
- const { join: join11 } = __require("node:path");
47436
- const credentialsPath = process.env.AWS_SHARED_CREDENTIALS_FILE || join11(homedir5(), ".aws", "credentials");
47563
+ const { join: join12 } = __require("node:path");
47564
+ const credentialsPath = process.env.AWS_SHARED_CREDENTIALS_FILE || join12(homedir5(), ".aws", "credentials");
47437
47565
  if (!existsSync15(credentialsPath))
47438
47566
  return null;
47439
- const content = readFileSync8(credentialsPath, "utf-8");
47567
+ const content = readFileSync9(credentialsPath, "utf-8");
47440
47568
  let currentProfile = null;
47441
47569
  let accessKeyId;
47442
47570
  let secretAccessKey;
@@ -47482,8 +47610,8 @@ function loadProfileFromFile(profile) {
47482
47610
  // src/aws/s3.ts
47483
47611
  import * as crypto3 from "node:crypto";
47484
47612
  import { readdir as readdir4 } from "node:fs/promises";
47485
- import { join as join12 } from "node:path";
47486
- import { readFileSync as readFileSync9 } from "node:fs";
47613
+ import { join as join13 } from "node:path";
47614
+ import { readFileSync as readFileSync10 } from "node:fs";
47487
47615
  function toFetchBody(data) {
47488
47616
  const { buffer, byteOffset, byteLength } = data;
47489
47617
  if (buffer instanceof ArrayBuffer) {
@@ -47885,7 +48013,7 @@ class S3Client2 {
47885
48013
  }
47886
48014
  }
47887
48015
  async copy(options) {
47888
- const fileContent = readFileSync9(options.source);
48016
+ const fileContent = readFileSync10(options.source);
47889
48017
  await this.putObject({
47890
48018
  bucket: options.bucket,
47891
48019
  key: options.key,
@@ -47908,7 +48036,7 @@ class S3Client2 {
47908
48036
  const relativePath = file.substring(options.source.length + 1);
47909
48037
  const s3Key = options.prefix ? `${options.prefix}/${relativePath}` : relativePath;
47910
48038
  if (!options.dryRun) {
47911
- const fileContent = readFileSync9(file);
48039
+ const fileContent = readFileSync10(file);
47912
48040
  await this.putObject({
47913
48041
  bucket: options.bucket,
47914
48042
  key: s3Key,
@@ -47939,7 +48067,7 @@ class S3Client2 {
47939
48067
  const files = [];
47940
48068
  const entries = await readdir4(dir, { withFileTypes: true });
47941
48069
  for (const entry of entries) {
47942
- const fullPath = join12(dir, entry.name);
48070
+ const fullPath = join13(dir, entry.name);
47943
48071
  if (entry.isDirectory()) {
47944
48072
  const subFiles = await this.listFilesRecursive(fullPath);
47945
48073
  files.push(...subFiles);
@@ -56895,13 +57023,13 @@ async function uploadStaticFiles(options) {
56895
57023
  const { sourceDir, bucket, region, cacheControl = "max-age=31536000, public", onProgress } = options;
56896
57024
  const s32 = new S3Client2(region);
56897
57025
  const { readdir: readdir5 } = await import("node:fs/promises");
56898
- const { join: join11, relative: relative5 } = await import("node:path");
57026
+ const { join: join12, relative: relative5 } = await import("node:path");
56899
57027
  const { createHash: createHash7 } = await import("node:crypto");
56900
57028
  async function listFiles(dir) {
56901
57029
  const files2 = [];
56902
57030
  const entries = await readdir5(dir, { withFileTypes: true });
56903
57031
  for (const entry of entries) {
56904
- const fullPath = join11(dir, entry.name);
57032
+ const fullPath = join12(dir, entry.name);
56905
57033
  if (entry.isDirectory()) {
56906
57034
  files2.push(...await listFiles(fullPath));
56907
57035
  } else {
@@ -57122,13 +57250,13 @@ var exports_serverless_image = {};
57122
57250
  __export(exports_serverless_image, {
57123
57251
  buildAndPushServerlessImage: () => buildAndPushServerlessImage
57124
57252
  });
57125
- import { execFileSync as execFileSync2, execSync as execSync3 } from "node:child_process";
57126
- import { mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as writeFileSync10 } from "node:fs";
57127
- import { tmpdir as tmpdir3 } from "node:os";
57128
- import { dirname as dirname7, join as join14 } from "node:path";
57253
+ import { execFileSync as execFileSync3, execSync as execSync3 } from "node:child_process";
57254
+ import { mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync4, rmSync as rmSync4, writeFileSync as writeFileSync10 } from "node:fs";
57255
+ import { tmpdir as tmpdir4 } from "node:os";
57256
+ import { dirname as dirname7, join as join15 } from "node:path";
57129
57257
  function writeEntries(baseDir, entries) {
57130
57258
  for (const e of entries) {
57131
- const dest = join14(baseDir, e.name);
57259
+ const dest = join15(baseDir, e.name);
57132
57260
  mkdirSync7(dirname7(dest), { recursive: true });
57133
57261
  writeFileSync10(dest, e.data, e.mode ? { mode: e.mode } : undefined);
57134
57262
  }
@@ -57139,9 +57267,9 @@ async function buildAndPushServerlessImage(opts) {
57139
57267
  const platform = arch === "arm64" ? "linux/arm64" : "linux/amd64";
57140
57268
  const isPhp = app.kind === "php";
57141
57269
  const step2 = opts.onStep ?? (() => {});
57142
- const stage = mkdtempSync3(join14(tmpdir3(), "tscloud-img-"));
57270
+ const stage = mkdtempSync4(join15(tmpdir4(), "tscloud-img-"));
57143
57271
  try {
57144
- const appDir = join14(stage, "app");
57272
+ const appDir = join15(stage, "app");
57145
57273
  mkdirSync7(appDir, { recursive: true });
57146
57274
  let handlers;
57147
57275
  let contentHash;
@@ -57150,19 +57278,19 @@ async function buildAndPushServerlessImage(opts) {
57150
57278
  step2("staging application tree");
57151
57279
  const entries = collectPhpAppEntries(projectRoot);
57152
57280
  writeEntries(appDir, entries);
57153
- writeEntries(join14(stage, "runtime"), phpRuntimeLayerAssets().map((a) => ({ name: a.path, data: a.contents, mode: a.mode })));
57281
+ writeEntries(join15(stage, "runtime"), phpRuntimeLayerAssets().map((a) => ({ name: a.path, data: a.contents, mode: a.mode })));
57154
57282
  const handler8 = app.handlers?.http ?? "public/index.php";
57155
57283
  handlers = { http: handler8, queue: app.handlers?.queue ?? handler8, cli: app.handlers?.cli ?? handler8 };
57156
57284
  contentHash = sha256(entries.map((e) => `${e.name}:${e.data.length}`).join("|"));
57157
57285
  } else {
57158
57286
  step2("bundling application");
57159
57287
  const artifact = await packageServerlessApp({ projectRoot, app, skipBuild: opts.skipBuild, onStep: step2 });
57160
- writeFileSync10(join14(appDir, "index.mjs"), artifact.bundle);
57288
+ writeFileSync10(join15(appDir, "index.mjs"), artifact.bundle);
57161
57289
  handlers = artifact.handlers;
57162
57290
  contentHash = artifact.sha256;
57163
57291
  }
57164
57292
  const tag = contentHash.slice(0, 16);
57165
- writeFileSync10(join14(stage, "Dockerfile"), generateAppImageDockerfile({
57293
+ writeFileSync10(join15(stage, "Dockerfile"), generateAppImageDockerfile({
57166
57294
  kind: app.kind ?? "node",
57167
57295
  phpVersion: app.phpVersion,
57168
57296
  architecture: arch
@@ -57184,16 +57312,16 @@ async function buildAndPushServerlessImage(opts) {
57184
57312
  throw new Error(`could not resolve ECR repository URI for ${repository}`);
57185
57313
  const imageUri = `${repoUri}:${tag}`;
57186
57314
  step2("docker build");
57187
- execFileSync2("docker", ["build", "--platform", platform, "-t", imageUri, stage], { stdio: "inherit" });
57315
+ execFileSync3("docker", ["build", "--platform", platform, "-t", imageUri, stage], { stdio: "inherit" });
57188
57316
  step2("ecr login");
57189
57317
  const loginCmd = await ecr2.getDockerLoginCommand();
57190
57318
  execSync3(loginCmd, { stdio: "inherit" });
57191
57319
  step2("docker push");
57192
- execFileSync2("docker", ["push", imageUri], { stdio: "inherit" });
57320
+ execFileSync3("docker", ["push", imageUri], { stdio: "inherit" });
57193
57321
  info(`Image: ${imageUri}`);
57194
57322
  return { imageUri, tag, handlers };
57195
57323
  } finally {
57196
- rmSync3(stage, { recursive: true, force: true });
57324
+ rmSync4(stage, { recursive: true, force: true });
57197
57325
  }
57198
57326
  }
57199
57327
  var init_serverless_image = __esm(async () => {
@@ -78421,7 +78549,7 @@ class ApplicationAutoScalingClient {
78421
78549
  init_s3();
78422
78550
  import * as net from "node:net";
78423
78551
  import * as tls from "node:tls";
78424
- import { readFileSync as readFileSync10 } from "node:fs";
78552
+ import { readFileSync as readFileSync11 } from "node:fs";
78425
78553
  import * as crypto4 from "node:crypto";
78426
78554
 
78427
78555
  class ImapServer {
@@ -78456,8 +78584,8 @@ class ImapServer {
78456
78584
  });
78457
78585
  if (this.config.tls?.key && this.config.tls?.cert) {
78458
78586
  const tlsOptions = {
78459
- key: readFileSync10(this.config.tls.key),
78460
- cert: readFileSync10(this.config.tls.cert)
78587
+ key: readFileSync11(this.config.tls.key),
78588
+ cert: readFileSync11(this.config.tls.cert)
78461
78589
  };
78462
78590
  this.tlsServer = tls.createServer(tlsOptions, (socket) => {
78463
78591
  this.handleConnection(socket);
@@ -79261,8 +79389,8 @@ class ImapServer {
79261
79389
  }
79262
79390
  this.send(session, `${tag} OK Begin TLS negotiation`);
79263
79391
  const tlsOptions = {
79264
- key: readFileSync10(this.config.tls.key),
79265
- cert: readFileSync10(this.config.tls.cert),
79392
+ key: readFileSync11(this.config.tls.key),
79393
+ cert: readFileSync11(this.config.tls.cert),
79266
79394
  isServer: true
79267
79395
  };
79268
79396
  const tlsSocket = new tls.TLSSocket(session.socket, tlsOptions);
@@ -79855,7 +79983,7 @@ init_ses();
79855
79983
  init_s3();
79856
79984
  import * as net2 from "node:net";
79857
79985
  import * as tls2 from "node:tls";
79858
- import { readFileSync as readFileSync11 } from "node:fs";
79986
+ import { readFileSync as readFileSync12 } from "node:fs";
79859
79987
  import * as crypto5 from "node:crypto";
79860
79988
 
79861
79989
  class SmtpServer {
@@ -79886,8 +80014,8 @@ class SmtpServer {
79886
80014
  });
79887
80015
  if (this.config.tls?.key && this.config.tls?.cert) {
79888
80016
  const tlsOptions = {
79889
- key: readFileSync11(this.config.tls.key),
79890
- cert: readFileSync11(this.config.tls.cert)
80017
+ key: readFileSync12(this.config.tls.key),
80018
+ cert: readFileSync12(this.config.tls.cert)
79891
80019
  };
79892
80020
  this.tlsServer = tls2.createServer(tlsOptions, (socket) => {
79893
80021
  this.handleConnection(socket, true);
@@ -80021,8 +80149,8 @@ class SmtpServer {
80021
80149
  }
80022
80150
  this.send(session, "220 Ready to start TLS");
80023
80151
  const tlsOptions = {
80024
- key: readFileSync11(this.config.tls.key),
80025
- cert: readFileSync11(this.config.tls.cert),
80152
+ key: readFileSync12(this.config.tls.key),
80153
+ cert: readFileSync12(this.config.tls.cert),
80026
80154
  isServer: true
80027
80155
  };
80028
80156
  const tlsSocket = new tls2.TLSSocket(session.socket, tlsOptions);
@@ -82721,11 +82849,11 @@ init_static_site_external_dns();
82721
82849
  init_static_site_external_dns();
82722
82850
  import process19 from "node:process";
82723
82851
  import { existsSync as existsSync16 } from "node:fs";
82724
- import { join as join13 } from "node:path";
82852
+ import { join as join14 } from "node:path";
82725
82853
  async function deploySite(config6) {
82726
82854
  const start = Date.now();
82727
82855
  const sourceDir = config6.sourceDir ?? "dist";
82728
- if (!existsSync16(join13(sourceDir, "index.html"))) {
82856
+ if (!existsSync16(join14(sourceDir, "index.html"))) {
82729
82857
  return fail(start, `${sourceDir}/ has no index.html — build first.`);
82730
82858
  }
82731
82859
  if (!process19.env.AWS_ACCESS_KEY_ID || !process19.env.AWS_SECRET_ACCESS_KEY) {
@@ -82796,8 +82924,8 @@ function fail(start, message) {
82796
82924
  // src/deploy/serverless-app.ts
82797
82925
  init_cloudformation();
82798
82926
  await init_dist2();
82799
- import { existsSync as existsSync17, readdirSync as readdirSync8, readFileSync as readFileSync12, statSync as statSync5 } from "node:fs";
82800
- import { join as join15, relative as relative6 } from "node:path";
82927
+ import { existsSync as existsSync17, readdirSync as readdirSync8, readFileSync as readFileSync13, statSync as statSync5 } from "node:fs";
82928
+ import { join as join16, relative as relative6 } from "node:path";
82801
82929
  init_s3();
82802
82930
  init_cli();
82803
82931
  function resolveContext(config6, environment) {
@@ -82925,7 +83053,7 @@ function contentType(file) {
82925
83053
  }
82926
83054
  function* walk3(dir) {
82927
83055
  for (const entry of readdirSync8(dir)) {
82928
- const full = join15(dir, entry);
83056
+ const full = join16(dir, entry);
82929
83057
  if (statSync5(full).isDirectory())
82930
83058
  yield* walk3(full);
82931
83059
  else
@@ -82939,7 +83067,7 @@ async function uploadAssets(s32, bucket, dir, prefix) {
82939
83067
  await s32.putObject({
82940
83068
  bucket,
82941
83069
  key,
82942
- body: readFileSync12(file),
83070
+ body: readFileSync13(file),
82943
83071
  contentType: contentType(file),
82944
83072
  cacheControl: "public, max-age=31536000, immutable"
82945
83073
  });
@@ -82966,11 +83094,15 @@ async function deployServerlessApp(config6, environment, opts = {}) {
82966
83094
  const lambda2 = new LambdaClient(region);
82967
83095
  const isPhp = app.kind === "php";
82968
83096
  const imageMode = app.packaging === "image";
83097
+ const resolved = resolveServerlessRuntime(app);
82969
83098
  let handlers;
82970
83099
  let codeSource;
82971
83100
  let artifactSha;
82972
83101
  let runtimeLayers;
82973
83102
  const parameters = [];
83103
+ if (imageMode && resolved.usesLayer && resolved.layerKind !== "php") {
83104
+ throw new Error(`packaging: 'image' currently supports PHP and managed Node runtimes. ` + `${resolved.kind}${resolved.kind === "node" ? ` ${resolved.version}` : ""} uses a custom runtime — ` + `use the default zip packaging (the runtime layer is built + attached automatically).`);
83105
+ }
82974
83106
  if (imageMode) {
82975
83107
  step("Building + pushing container image");
82976
83108
  const { buildAndPushServerlessImage: buildAndPushServerlessImage2 } = await init_serverless_image().then(() => exports_serverless_image);
@@ -82993,10 +83125,12 @@ async function deployServerlessApp(config6, environment, opts = {}) {
82993
83125
  info(`Artifact: ${artifact.sha256.slice(0, 12)} (${(artifact.zip.length / 1024).toFixed(0)} KB)`);
82994
83126
  handlers = artifact.handlers;
82995
83127
  artifactSha = artifact.sha256;
82996
- if (isPhp) {
82997
- runtimeLayers = app.layers ?? (process.env.TSCLOUD_PHP_LAYER_ARN ? [process.env.TSCLOUD_PHP_LAYER_ARN] : undefined);
83128
+ if (resolved.usesLayer) {
83129
+ const envArn = resolved.layerEnvVar ? process.env[resolved.layerEnvVar] : undefined;
83130
+ runtimeLayers = app.layers ?? (envArn ? [envArn] : undefined);
82998
83131
  if (!runtimeLayers?.length) {
82999
- throw new Error("PHP serverless apps need a runtime layer. Set environments.<env>.app.layers or the TSCLOUD_PHP_LAYER_ARN env var (build one with `cloud serverless:build-php-layer`), or use packaging: 'image'.");
83132
+ const buildCmd = resolved.layerKind === "php" ? "serverless:build-php-layer" : resolved.layerKind === "bun" ? "serverless:build-bun-layer" : "serverless:build-node-layer";
83133
+ throw new Error(`${resolved.kind} apps on provided.al2023 need a runtime layer. Set environments.<env>.app.layers or the ${resolved.layerEnvVar} env var (build one with \`cloud ${buildCmd}\`)${resolved.kind === "node" ? ", or use a managed Node version (18/20/22)" : ""}.`);
83000
83134
  }
83001
83135
  }
83002
83136
  if (!await s32.bucketExists(artifactBucket)) {
@@ -83036,15 +83170,15 @@ async function deployServerlessApp(config6, environment, opts = {}) {
83036
83170
  const outputs = await cfn.getStackOutputs(stackName).catch(() => ({}));
83037
83171
  let assetUrl;
83038
83172
  if (app.assets) {
83039
- const assetsDir2 = join15(projectRoot, app.assets);
83040
- if (existsSync17(assetsDir2)) {
83173
+ const assetsDir3 = join16(projectRoot, app.assets);
83174
+ if (existsSync17(assetsDir3)) {
83041
83175
  const cdn = outputs.AssetsCdnDomain;
83042
83176
  assetUrl = cdn ? `https://${cdn}/${artifactSha}` : undefined;
83043
83177
  step(`Syncing assets from ${app.assets}`);
83044
- const n = await uploadAssets(s32, ctx.assetsBucket, assetsDir2, artifactSha);
83178
+ const n = await uploadAssets(s32, ctx.assetsBucket, assetsDir3, artifactSha);
83045
83179
  info(`Uploaded ${n} asset(s)${assetUrl ? ` → ${assetUrl}` : ""}`);
83046
83180
  } else {
83047
- warn(`Assets directory not found: ${assetsDir2}`);
83181
+ warn(`Assets directory not found: ${assetsDir3}`);
83048
83182
  }
83049
83183
  }
83050
83184
  step("Resolving secrets");
@@ -83185,10 +83319,26 @@ await init_dist2();
83185
83319
  // src/drivers/aws/driver.ts
83186
83320
  init_cloudformation();
83187
83321
  await init_dist2();
83188
- import { readFileSync as readFileSync13 } from "node:fs";
83322
+ import { readFileSync as readFileSync14 } from "node:fs";
83189
83323
  init_s3();
83190
83324
 
83191
83325
  // src/drivers/shared/package-manager.ts
83326
+ var PANTRY_PACKAGES = {
83327
+ php: "php.net",
83328
+ nginx: "nginx.org",
83329
+ composer: "getcomposer.org",
83330
+ mysql: "mysql.com",
83331
+ mariadb: "mariadb.com/server",
83332
+ postgres: "postgresql.org",
83333
+ redis: "redis.io",
83334
+ memcached: "memcached.org",
83335
+ meilisearch: "meilisearch.com",
83336
+ git: "git-scm.com",
83337
+ certbot: "certbot.eff.org",
83338
+ bun: "bun.sh",
83339
+ node: "nodejs.org",
83340
+ deno: "deno.land"
83341
+ };
83192
83342
  var PANTRY_INSTALL_DIR = "/usr/local/bin";
83193
83343
  var PANTRY_PROJECT_DIR = "/opt/pantry";
83194
83344
  function sh(value) {
@@ -83228,10 +83378,6 @@ function buildPantryServiceScript(services) {
83228
83378
  function enabled(spec) {
83229
83379
  return spec === true || typeof spec === "object" && spec != null;
83230
83380
  }
83231
- function sq(value) {
83232
- const escaped = value.split("'").join("'\\''");
83233
- return `'${escaped}'`;
83234
- }
83235
83381
  function planServices(services) {
83236
83382
  const packages = [];
83237
83383
  const names = [];
@@ -83239,7 +83385,7 @@ function planServices(services) {
83239
83385
  packages.push("mysql.com");
83240
83386
  names.push("mysql");
83241
83387
  } else if (enabled(services.mariadb)) {
83242
- packages.push("mariadb.org");
83388
+ packages.push(PANTRY_PACKAGES.mariadb);
83243
83389
  names.push("mariadb");
83244
83390
  }
83245
83391
  if (enabled(services.postgres)) {
@@ -83281,17 +83427,26 @@ function buildDatabaseSetupScript(database, services = {}) {
83281
83427
  const useMariadb = enabled(services.mariadb) || database.engine === "mariadb";
83282
83428
  const useMysql = enabled(services.mysql) || database.engine === "mysql";
83283
83429
  if (usePostgres && !useMysql && !useMariadb) {
83284
- const psql = "psql -h 127.0.0.1 -p 5432 -U postgres";
83430
+ const pgIdent = (v) => `"${v.replace(/"/g, '""')}"`;
83431
+ const pgLit = (v) => `'${v.replace(/'/g, "''")}'`;
83285
83432
  return [
83286
83433
  pantryEnvActivation(),
83287
- `${psql} -tc "SELECT 1 FROM pg_roles WHERE rolname=${sq(user)}" | grep -q 1 ` + `|| ${psql} -c "CREATE ROLE ${sq(user)} LOGIN PASSWORD ${sq(pass)};"`,
83288
- `${psql} -tc "SELECT 1 FROM pg_database WHERE datname=${sq(name)}" | grep -q 1 ` + `|| ${psql} -c "CREATE DATABASE ${sq(name)} OWNER ${sq(user)};"`
83434
+ "for i in $(seq 1 30); do pg_isready -h 127.0.0.1 -p 5432 -q && break; sleep 2; done",
83435
+ "psql -h 127.0.0.1 -p 5432 -U postgres <<'TS_CLOUD_PG_EOF'",
83436
+ "DO $$ BEGIN",
83437
+ ` IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = ${pgLit(user)}) THEN`,
83438
+ ` CREATE ROLE ${pgIdent(user)} LOGIN PASSWORD ${pgLit(pass)};`,
83439
+ " END IF;",
83440
+ "END $$;",
83441
+ `SELECT 'CREATE DATABASE ${pgIdent(name)} OWNER ${pgIdent(user)}' ` + `WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = ${pgLit(name)})\\gexec`,
83442
+ "TS_CLOUD_PG_EOF"
83289
83443
  ];
83290
83444
  }
83291
83445
  const ident = (v) => v.replace(/`/g, "``");
83292
83446
  const lit = (v) => v.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
83293
83447
  return [
83294
83448
  pantryEnvActivation(),
83449
+ "for i in $(seq 1 30); do mysqladmin -h 127.0.0.1 -P 3306 -u root ping 2>/dev/null | grep -q alive && break; sleep 2; done",
83295
83450
  "mysql -h 127.0.0.1 -P 3306 -u root <<'TS_CLOUD_SQL_EOF'",
83296
83451
  `CREATE DATABASE IF NOT EXISTS \`${ident(name)}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;`,
83297
83452
  `CREATE USER IF NOT EXISTS '${lit(user)}'@'%' IDENTIFIED BY '${lit(pass)}';`,
@@ -83438,9 +83593,9 @@ function buildNginxVhostScript(options) {
83438
83593
  const out = [];
83439
83594
  if (options.auth) {
83440
83595
  const file = htpasswdPath(options.siteName);
83441
- const sq2 = (v) => v.split("'").join("'\\''");
83442
- const pw = sq2(options.auth.password);
83443
- const user = sq2(options.auth.username);
83596
+ const sq = (v) => v.split("'").join("'\\''");
83597
+ const pw = sq(options.auth.password);
83598
+ const user = sq(options.auth.username);
83444
83599
  out.push(`TS_CLOUD_HTPASS=$(openssl passwd -apr1 '${pw}')`, `printf '%s:%s\\n' '${user}' "$TS_CLOUD_HTPASS" > ${file}`, `chmod 640 ${file}`, `chown root:www-data ${file} 2>/dev/null || true`);
83445
83600
  }
83446
83601
  out.push(`cat > ${available} <<'TS_CLOUD_NGINX_EOF'`, vhost.replace(/\n$/, ""), "TS_CLOUD_NGINX_EOF", `ln -sf ${available} ${enabled2}`, "rm -f /etc/nginx/sites-enabled/default", `${NGINX_WRAPPER} -t`, "systemctl reload ts-cloud-nginx 2>/dev/null || systemctl restart ts-cloud-nginx 2>/dev/null || true");
@@ -84204,7 +84359,7 @@ class AwsDriver {
84204
84359
  await s32.putObject({
84205
84360
  bucket,
84206
84361
  key: options.remoteKey,
84207
- body: readFileSync13(options.localPath),
84362
+ body: readFileSync14(options.localPath),
84208
84363
  contentType: "application/gzip"
84209
84364
  });
84210
84365
  return { artifactRef: `s3://${bucket}/${options.remoteKey}` };
@@ -84311,9 +84466,9 @@ class AwsDriver {
84311
84466
 
84312
84467
  // src/drivers/hetzner/driver.ts
84313
84468
  await init_dist2();
84314
- import { existsSync as existsSync18, readFileSync as readFileSync14 } from "node:fs";
84469
+ import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
84315
84470
  import { homedir as homedir7 } from "node:os";
84316
- import { join as join17 } from "node:path";
84471
+ import { join as join18 } from "node:path";
84317
84472
  import { execSync as execSync5 } from "node:child_process";
84318
84473
 
84319
84474
  // src/drivers/hetzner/client.ts
@@ -84743,10 +84898,10 @@ function matchesTsCloudLabels(labels, slug, environment, role = "app") {
84743
84898
 
84744
84899
  // src/drivers/hetzner/state.ts
84745
84900
  import { mkdir as mkdir4, readFile, writeFile as writeFile4 } from "node:fs/promises";
84746
- import { join as join16 } from "node:path";
84901
+ import { join as join17 } from "node:path";
84747
84902
  var STATE_DIR = ".ts-cloud/state";
84748
84903
  function driverStatePath(stackName) {
84749
- return join16(process.cwd(), STATE_DIR, `${stackName}.json`);
84904
+ return join17(process.cwd(), STATE_DIR, `${stackName}.json`);
84750
84905
  }
84751
84906
  async function readDriverState(stackName) {
84752
84907
  try {
@@ -84758,7 +84913,7 @@ async function readDriverState(stackName) {
84758
84913
  }
84759
84914
  async function writeDriverState(stackName, state) {
84760
84915
  const path = driverStatePath(stackName);
84761
- await mkdir4(join16(process.cwd(), STATE_DIR), { recursive: true });
84916
+ await mkdir4(join17(process.cwd(), STATE_DIR), { recursive: true });
84762
84917
  await writeFile4(path, `${JSON.stringify(state, null, 2)}
84763
84918
  `, "utf8");
84764
84919
  }
@@ -84766,7 +84921,7 @@ async function writeDriverState(stackName, state) {
84766
84921
  // src/drivers/hetzner/driver.ts
84767
84922
  var SSH_MAX_BUFFER = 1024 * 1024 * 256;
84768
84923
  function expandHome(path) {
84769
- return path.startsWith("~/") ? join17(homedir7(), path.slice(2)) : path;
84924
+ return path.startsWith("~/") ? join18(homedir7(), path.slice(2)) : path;
84770
84925
  }
84771
84926
 
84772
84927
  class HetznerDriver {
@@ -84838,7 +84993,7 @@ class HetznerDriver {
84838
84993
  }) : undefined;
84839
84994
  const provision = buildComputeProvisionScripts(config6);
84840
84995
  const baked = compute.bakedImage === true;
84841
- const bootstrap = buildUbuntuBootstrapScript({
84996
+ const bootstrap2 = buildUbuntuBootstrapScript({
84842
84997
  runtime: provision.runtime,
84843
84998
  runtimeVersion: provision.runtimeVersion,
84844
84999
  systemPackages: compute.systemPackages,
@@ -84848,7 +85003,7 @@ class HetznerDriver {
84848
85003
  rpxProvision,
84849
85004
  baked
84850
85005
  });
84851
- const userData = wrapCloudInitUserData(bootstrap);
85006
+ const userData = wrapCloudInitUserData(bootstrap2);
84852
85007
  const serverType = resolveHetznerServerType(compute.size);
84853
85008
  const image = compute.image || config6.hetzner?.image || "ubuntu-24.04";
84854
85009
  const firewallName = `${slug}-${environment}-app-fw`;
@@ -85197,7 +85352,7 @@ class HetznerDriver {
85197
85352
  if (!existsSync18(this.sshPublicKeyPath)) {
85198
85353
  throw new Error(`SSH public key not found at ${this.sshPublicKeyPath}. ts-cloud deploys to Hetzner over SSH and needs a public key to authorize on the server. ` + `Generate one (\`ssh-keygen -t ed25519\`) or set hetzner.sshPrivateKeyPath / HCLOUD_SSH_PUBLIC_KEY.`);
85199
85354
  }
85200
- const publicKey = readFileSync14(this.sshPublicKeyPath, "utf8").trim();
85355
+ const publicKey = readFileSync15(this.sshPublicKeyPath, "utf8").trim();
85201
85356
  const normalized = normalizeSshPublicKey(publicKey);
85202
85357
  const existing = await this.client.listSshKeys();
85203
85358
  const match = existing.find((key) => normalizeSshPublicKey(key.public_key) === normalized);
@@ -86077,6 +86232,7 @@ export {
86077
86232
  stackDependencyManager,
86078
86233
  signRequestAsync,
86079
86234
  signRequest,
86235
+ sharedRuntimeLoop,
86080
86236
  sha256,
86081
86237
  setupDns01Challenge,
86082
86238
  setMaintenance,
@@ -86103,6 +86259,7 @@ export {
86103
86259
  resolveSiteKind,
86104
86260
  resolveSiteDeployTarget,
86105
86261
  resolveSiteBucketName,
86262
+ resolveServerlessRuntime,
86106
86263
  resolveServerlessAssetBucketName,
86107
86264
  resolveServerlessArtifactBucketName,
86108
86265
  resolveServerlessAppStackName,
@@ -86333,8 +86490,10 @@ export {
86333
86490
  buildSiteDeployScript,
86334
86491
  buildPhpRuntimeLayerZip,
86335
86492
  buildOptimizationManager,
86493
+ buildNodeRuntimeLayerZip,
86336
86494
  buildFunctionEnv,
86337
86495
  buildCloudFormationTemplate,
86496
+ buildBunRuntimeLayerZip,
86338
86497
  buildAndPushServerlessImage,
86339
86498
  bounceComplaintHandler,
86340
86499
  blueGreenManager,
@@ -86439,6 +86598,7 @@ export {
86439
86598
  MigrationManager,
86440
86599
  MetricsManager,
86441
86600
  Messaging,
86601
+ MANAGED_NODE_VERSIONS,
86442
86602
  LogsManager,
86443
86603
  LambdaVersionsManager,
86444
86604
  LambdaVPCManager,