@stacksolo/cli 0.1.2 → 0.1.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/dist/index.js CHANGED
@@ -275,6 +275,7 @@ var init_plugin_loader_service = __esm({
275
275
 
276
276
  // src/index.ts
277
277
  import { Command as Command24 } from "commander";
278
+ import { createRequire } from "module";
278
279
 
279
280
  // src/commands/project/init.ts
280
281
  import { Command } from "commander";
@@ -876,25 +877,28 @@ async function scaffoldTemplates(cwd, projectType, uiFramework) {
876
877
  }
877
878
  async function scaffoldFunctionApi(cwd, name) {
878
879
  const dir = path.join(cwd, name);
879
- await fs.mkdir(dir, { recursive: true });
880
+ const srcDir = path.join(dir, "src");
881
+ await fs.mkdir(srcDir, { recursive: true });
880
882
  await fs.writeFile(
881
883
  path.join(dir, "package.json"),
882
884
  JSON.stringify(
883
885
  {
884
886
  name,
885
887
  version: "1.0.0",
886
- main: "index.js",
888
+ main: "dist/index.js",
887
889
  type: "module",
888
890
  scripts: {
889
- start: "npx functions-framework --target=handler",
890
- build: "tsc"
891
+ dev: "tsup src/index.ts --format esm --target node20 --watch --onSuccess 'functions-framework --source=dist --target=handler'",
892
+ build: "tsup src/index.ts --format esm --target node20",
893
+ start: "functions-framework --source=dist --target=handler"
891
894
  },
892
895
  dependencies: {
893
896
  "@google-cloud/functions-framework": "^3.3.0"
894
897
  },
895
898
  devDependencies: {
896
899
  typescript: "^5.0.0",
897
- "@types/node": "^20.0.0"
900
+ "@types/node": "^20.0.0",
901
+ tsup: "^8.0.0"
898
902
  }
899
903
  },
900
904
  null,
@@ -902,15 +906,21 @@ async function scaffoldFunctionApi(cwd, name) {
902
906
  ) + "\n"
903
907
  );
904
908
  await fs.writeFile(
905
- path.join(dir, "index.ts"),
906
- `import functions from '@google-cloud/functions-framework';
909
+ path.join(srcDir, "index.ts"),
910
+ `/**
911
+ * ${name} HTTP function
912
+ * Generated by: stacksolo init
913
+ */
914
+
915
+ import type { Request, Response } from '@google-cloud/functions-framework';
907
916
 
908
- functions.http('handler', (req, res) => {
917
+ export function handler(req: Request, res: Response): void {
909
918
  const { method, path } = req;
910
919
 
911
920
  // Health check
912
921
  if (path === '/health') {
913
- return res.json({ status: 'ok' });
922
+ res.json({ status: 'ok' });
923
+ return;
914
924
  }
915
925
 
916
926
  // Your API routes here
@@ -919,7 +929,7 @@ functions.http('handler', (req, res) => {
919
929
  method,
920
930
  path,
921
931
  });
922
- });
932
+ }
923
933
  `
924
934
  );
925
935
  await fs.writeFile(
@@ -929,19 +939,31 @@ functions.http('handler', (req, res) => {
929
939
  compilerOptions: {
930
940
  target: "ES2022",
931
941
  module: "ESNext",
932
- moduleResolution: "node",
942
+ moduleResolution: "bundler",
933
943
  esModuleInterop: true,
934
944
  strict: true,
945
+ skipLibCheck: true,
935
946
  outDir: "dist",
947
+ rootDir: "src",
936
948
  declaration: true
937
949
  },
938
- include: ["*.ts"]
950
+ include: ["src/**/*"],
951
+ exclude: ["node_modules", "dist"]
939
952
  },
940
953
  null,
941
954
  2
942
955
  ) + "\n"
943
956
  );
944
- return [`${name}/package.json`, `${name}/index.ts`, `${name}/tsconfig.json`];
957
+ await fs.writeFile(
958
+ path.join(dir, ".gitignore"),
959
+ `node_modules/
960
+ dist/
961
+ *.log
962
+ .env
963
+ .env.local
964
+ `
965
+ );
966
+ return [`${name}/package.json`, `${name}/src/index.ts`, `${name}/tsconfig.json`, `${name}/.gitignore`];
945
967
  }
946
968
  async function scaffoldContainerApi(cwd, name) {
947
969
  const dir = path.join(cwd, name);
@@ -1026,25 +1048,28 @@ CMD ["node", "index.js"]
1026
1048
  }
1027
1049
  async function scaffoldFunctionCron(cwd, name) {
1028
1050
  const dir = path.join(cwd, name);
1029
- await fs.mkdir(dir, { recursive: true });
1051
+ const srcDir = path.join(dir, "src");
1052
+ await fs.mkdir(srcDir, { recursive: true });
1030
1053
  await fs.writeFile(
1031
1054
  path.join(dir, "package.json"),
1032
1055
  JSON.stringify(
1033
1056
  {
1034
1057
  name,
1035
1058
  version: "1.0.0",
1036
- main: "index.js",
1059
+ main: "dist/index.js",
1037
1060
  type: "module",
1038
1061
  scripts: {
1039
- start: "npx functions-framework --target=handler",
1040
- build: "tsc"
1062
+ dev: "tsup src/index.ts --format esm --target node20 --watch --onSuccess 'functions-framework --source=dist --target=handler'",
1063
+ build: "tsup src/index.ts --format esm --target node20",
1064
+ start: "functions-framework --source=dist --target=handler"
1041
1065
  },
1042
1066
  dependencies: {
1043
1067
  "@google-cloud/functions-framework": "^3.3.0"
1044
1068
  },
1045
1069
  devDependencies: {
1046
1070
  typescript: "^5.0.0",
1047
- "@types/node": "^20.0.0"
1071
+ "@types/node": "^20.0.0",
1072
+ tsup: "^8.0.0"
1048
1073
  }
1049
1074
  },
1050
1075
  null,
@@ -1052,17 +1077,22 @@ async function scaffoldFunctionCron(cwd, name) {
1052
1077
  ) + "\n"
1053
1078
  );
1054
1079
  await fs.writeFile(
1055
- path.join(dir, "index.ts"),
1056
- `import functions from '@google-cloud/functions-framework';
1080
+ path.join(srcDir, "index.ts"),
1081
+ `/**
1082
+ * ${name} Cron function
1083
+ * Generated by: stacksolo init
1084
+ */
1085
+
1086
+ import type { Request, Response } from '@google-cloud/functions-framework';
1057
1087
 
1058
- functions.http('handler', (req, res) => {
1088
+ export function handler(req: Request, res: Response): void {
1059
1089
  console.log('Cron triggered:', new Date().toISOString());
1060
1090
 
1061
1091
  // Your scheduled job logic here
1062
1092
 
1063
1093
  console.log('Job complete');
1064
1094
  res.json({ status: 'ok' });
1065
- });
1095
+ }
1066
1096
  `
1067
1097
  );
1068
1098
  await fs.writeFile(
@@ -1072,19 +1102,31 @@ functions.http('handler', (req, res) => {
1072
1102
  compilerOptions: {
1073
1103
  target: "ES2022",
1074
1104
  module: "ESNext",
1075
- moduleResolution: "node",
1105
+ moduleResolution: "bundler",
1076
1106
  esModuleInterop: true,
1077
1107
  strict: true,
1108
+ skipLibCheck: true,
1078
1109
  outDir: "dist",
1110
+ rootDir: "src",
1079
1111
  declaration: true
1080
1112
  },
1081
- include: ["*.ts"]
1113
+ include: ["src/**/*"],
1114
+ exclude: ["node_modules", "dist"]
1082
1115
  },
1083
1116
  null,
1084
1117
  2
1085
1118
  ) + "\n"
1086
1119
  );
1087
- return [`${name}/package.json`, `${name}/index.ts`, `${name}/tsconfig.json`];
1120
+ await fs.writeFile(
1121
+ path.join(dir, ".gitignore"),
1122
+ `node_modules/
1123
+ dist/
1124
+ *.log
1125
+ .env
1126
+ .env.local
1127
+ `
1128
+ );
1129
+ return [`${name}/package.json`, `${name}/src/index.ts`, `${name}/tsconfig.json`, `${name}/.gitignore`];
1088
1130
  }
1089
1131
  async function scaffoldStaticWeb(cwd, name) {
1090
1132
  const dir = path.join(cwd, name);
@@ -6599,7 +6641,7 @@ CMD ["node", "dist/index.js"]
6599
6641
  `;
6600
6642
  }
6601
6643
  function generateContainerPackageJson(container, config) {
6602
- const pkg = {
6644
+ const pkg2 = {
6603
6645
  name: `@${config.project.name}/${container.name}`,
6604
6646
  version: "0.1.0",
6605
6647
  private: true,
@@ -6622,7 +6664,7 @@ function generateContainerPackageJson(container, config) {
6622
6664
  typescript: "^5.3.0"
6623
6665
  }
6624
6666
  };
6625
- return JSON.stringify(pkg, null, 2) + "\n";
6667
+ return JSON.stringify(pkg2, null, 2) + "\n";
6626
6668
  }
6627
6669
  function generateTsConfig() {
6628
6670
  const config = {
@@ -6732,7 +6774,7 @@ function generateFunctionScaffold(func, config) {
6732
6774
  };
6733
6775
  }
6734
6776
  function generateFunctionPackageJson(func, config) {
6735
- const pkg = {
6777
+ const pkg2 = {
6736
6778
  name: `@${config.project.name}/${func.name}`,
6737
6779
  version: "0.1.0",
6738
6780
  private: true,
@@ -6756,7 +6798,7 @@ function generateFunctionPackageJson(func, config) {
6756
6798
  typescript: "^5.3.0"
6757
6799
  }
6758
6800
  };
6759
- return JSON.stringify(pkg, null, 2) + "\n";
6801
+ return JSON.stringify(pkg2, null, 2) + "\n";
6760
6802
  }
6761
6803
  function generateTsConfig2() {
6762
6804
  const config = {
@@ -7728,7 +7770,7 @@ CMD ["./start.sh"]
7728
7770
  `;
7729
7771
  }
7730
7772
  function generatePackageJson(kernel, config) {
7731
- const pkg = {
7773
+ const pkg2 = {
7732
7774
  name: `@${config.project.name}/${kernel.name}`,
7733
7775
  version: "0.1.0",
7734
7776
  description: "Kernel service - HTTP + NATS hybrid",
@@ -7755,7 +7797,7 @@ function generatePackageJson(kernel, config) {
7755
7797
  typescript: "^5.0.0"
7756
7798
  }
7757
7799
  };
7758
- return JSON.stringify(pkg, null, 2) + "\n";
7800
+ return JSON.stringify(pkg2, null, 2) + "\n";
7759
7801
  }
7760
7802
  function generateTsConfig3() {
7761
7803
  const config = {
@@ -9302,17 +9344,17 @@ async function deployConfig(config, _stateDir, options = {}) {
9302
9344
  await fs7.mkdir(stagingDir, { recursive: true });
9303
9345
  await execAsync5(`cp -r "${distDir}"/* "${stagingDir}"/`, { timeout: 3e4 });
9304
9346
  const pkgContent = await fs7.readFile(packageJsonPath, "utf-8");
9305
- const pkg = JSON.parse(pkgContent);
9306
- if (pkg.main && pkg.main.startsWith("dist/")) {
9307
- pkg.main = pkg.main.replace("dist/", "");
9347
+ const pkg2 = JSON.parse(pkgContent);
9348
+ if (pkg2.main && pkg2.main.startsWith("dist/")) {
9349
+ pkg2.main = pkg2.main.replace("dist/", "");
9308
9350
  }
9309
- if (pkg.scripts) {
9310
- delete pkg.scripts.build;
9311
- delete pkg.scripts.typecheck;
9312
- delete pkg.scripts.dev;
9351
+ if (pkg2.scripts) {
9352
+ delete pkg2.scripts.build;
9353
+ delete pkg2.scripts.typecheck;
9354
+ delete pkg2.scripts.dev;
9313
9355
  }
9314
- delete pkg.devDependencies;
9315
- await fs7.writeFile(path9.join(stagingDir, "package.json"), JSON.stringify(pkg, null, 2));
9356
+ delete pkg2.devDependencies;
9357
+ await fs7.writeFile(path9.join(stagingDir, "package.json"), JSON.stringify(pkg2, null, 2));
9316
9358
  await execAsync5(`cd "${stagingDir}" && zip -r "${sourceZipPath}" .`, { timeout: 6e4 });
9317
9359
  await fs7.rm(stagingDir, { recursive: true, force: true });
9318
9360
  } else {
@@ -9359,7 +9401,7 @@ async function deployConfig(config, _stateDir, options = {}) {
9359
9401
  const packageJsonPath = path9.join(kernelSourceDir, "package.json");
9360
9402
  try {
9361
9403
  const packageJsonContent = await fs7.readFile(packageJsonPath, "utf-8");
9362
- const pkg = JSON.parse(packageJsonContent);
9404
+ const pkg2 = JSON.parse(packageJsonContent);
9363
9405
  const nodeModulesPath = path9.join(kernelSourceDir, "node_modules");
9364
9406
  try {
9365
9407
  await fs7.access(nodeModulesPath);
@@ -9367,7 +9409,7 @@ async function deployConfig(config, _stateDir, options = {}) {
9367
9409
  log(`Installing dependencies for GCP Kernel...`);
9368
9410
  await execAsync5("npm install", { cwd: kernelSourceDir, timeout: 12e4 });
9369
9411
  }
9370
- if (pkg.scripts?.build) {
9412
+ if (pkg2.scripts?.build) {
9371
9413
  log(`Building GCP Kernel TypeScript...`);
9372
9414
  await execAsync5("npm run build", { cwd: kernelSourceDir, timeout: 6e4 });
9373
9415
  }
@@ -9553,7 +9595,7 @@ terraform {
9553
9595
  const packageJsonPath = path9.join(sourceDir, "package.json");
9554
9596
  try {
9555
9597
  const packageJsonContent = await fs7.readFile(packageJsonPath, "utf-8");
9556
- const pkg = JSON.parse(packageJsonContent);
9598
+ const pkg2 = JSON.parse(packageJsonContent);
9557
9599
  const nodeModulesPath = path9.join(sourceDir, "node_modules");
9558
9600
  try {
9559
9601
  await fs7.access(nodeModulesPath);
@@ -9561,7 +9603,7 @@ terraform {
9561
9603
  log(`Installing dependencies for ${containerName}...`);
9562
9604
  await execAsync5("npm install", { cwd: sourceDir, timeout: 12e4 });
9563
9605
  }
9564
- if (pkg.scripts?.build) {
9606
+ if (pkg2.scripts?.build) {
9565
9607
  log(`Building ${containerName}...`);
9566
9608
  await execAsync5("npm run build", { cwd: sourceDir, timeout: 6e4 });
9567
9609
  }
@@ -9635,12 +9677,12 @@ terraform {
9635
9677
  const packageJsonPath = path9.join(sourceDir, "package.json");
9636
9678
  try {
9637
9679
  const pkgContent = await fs7.readFile(packageJsonPath, "utf-8");
9638
- const pkg = JSON.parse(pkgContent);
9639
- if (pkg.dependencies?.["@sveltejs/kit"] || pkg.devDependencies?.["@sveltejs/kit"]) {
9680
+ const pkg2 = JSON.parse(pkgContent);
9681
+ if (pkg2.dependencies?.["@sveltejs/kit"] || pkg2.devDependencies?.["@sveltejs/kit"]) {
9640
9682
  detectedFramework = "sveltekit";
9641
- } else if (pkg.dependencies?.vue || pkg.devDependencies?.vue) {
9683
+ } else if (pkg2.dependencies?.vue || pkg2.devDependencies?.vue) {
9642
9684
  detectedFramework = "vue";
9643
- } else if (pkg.dependencies?.react || pkg.devDependencies?.react) {
9685
+ } else if (pkg2.dependencies?.react || pkg2.devDependencies?.react) {
9644
9686
  detectedFramework = "react";
9645
9687
  }
9646
9688
  } catch {
@@ -15551,6 +15593,9 @@ function generateConfigMap(options) {
15551
15593
  } else {
15552
15594
  data.PUBSUB_EMULATOR_HOST = "pubsub-emulator:8085";
15553
15595
  }
15596
+ if (options.kernelUrl) {
15597
+ data.KERNEL_URL = options.kernelUrl;
15598
+ }
15554
15599
  if (options.additionalEnv) {
15555
15600
  Object.assign(data, options.additionalEnv);
15556
15601
  }
@@ -15659,9 +15704,31 @@ function generateFunctionManifests(options) {
15659
15704
  const functionName = sanitizeName(options.function.name);
15660
15705
  const runtimeConfig = getRuntimeConfig(options.function.runtime, options.function.entryPoint);
15661
15706
  const isPython = isPythonRuntime(options.function.runtime);
15662
- const installCmd = isPython ? "pip install -r requirements.txt 2>/dev/null || true && pip install functions-framework" : "npm install";
15663
15707
  const runCmd = isPython ? runtimeConfig.command.join(" ") : "npm run dev 2>/dev/null || " + runtimeConfig.command.join(" ");
15664
- const containerCommand = ["sh", "-c", `${installCmd} && ${runCmd}`];
15708
+ const containerCommand = isPython ? [
15709
+ "sh",
15710
+ "-c",
15711
+ [
15712
+ "cp -r /source/* /app/ 2>/dev/null || true",
15713
+ "cd /app",
15714
+ "pip install -r requirements.txt 2>/dev/null || true",
15715
+ "pip install functions-framework",
15716
+ runCmd
15717
+ ].join(" && ")
15718
+ ] : [
15719
+ "sh",
15720
+ "-c",
15721
+ [
15722
+ // Copy source files to /app, excluding node_modules (macOS binaries don't work in Linux)
15723
+ "cd /source",
15724
+ "find . -maxdepth 1 ! -name node_modules ! -name . -exec cp -r {} /app/ \\;",
15725
+ "cd /app",
15726
+ // Always install fresh for Linux platform
15727
+ "npm install",
15728
+ // Run the dev server
15729
+ runCmd
15730
+ ].join(" && ")
15731
+ ];
15665
15732
  const labels = {
15666
15733
  "app.kubernetes.io/name": functionName,
15667
15734
  "app.kubernetes.io/component": "function",
@@ -15710,16 +15777,21 @@ function generateFunctionManifests(options) {
15710
15777
  volumeMounts: [
15711
15778
  {
15712
15779
  name: "source",
15780
+ mountPath: "/source",
15781
+ readOnly: true
15782
+ },
15783
+ {
15784
+ name: "workdir",
15713
15785
  mountPath: "/app"
15714
15786
  }
15715
15787
  ],
15716
15788
  workingDir: "/app",
15717
15789
  resources: {
15718
15790
  limits: {
15719
- memory: options.function.memory || "256Mi"
15791
+ memory: options.function.memory || "512Mi"
15720
15792
  },
15721
15793
  requests: {
15722
- memory: "128Mi"
15794
+ memory: "256Mi"
15723
15795
  }
15724
15796
  }
15725
15797
  }
@@ -15731,6 +15803,10 @@ function generateFunctionManifests(options) {
15731
15803
  path: options.sourceDir,
15732
15804
  type: "DirectoryOrCreate"
15733
15805
  }
15806
+ },
15807
+ {
15808
+ name: "workdir",
15809
+ emptyDir: {}
15734
15810
  }
15735
15811
  ]
15736
15812
  }
@@ -15816,7 +15892,21 @@ function generateUIManifests(options) {
15816
15892
  {
15817
15893
  name: uiName,
15818
15894
  image: "node:20-slim",
15819
- command: ["sh", "-c", `npm install && ${frameworkConfig.command.join(" ")}`],
15895
+ // Copy source (excluding node_modules) to working directory, then install fresh Linux deps
15896
+ command: [
15897
+ "sh",
15898
+ "-c",
15899
+ [
15900
+ // Copy source files to /app, excluding node_modules (macOS binaries don't work in Linux)
15901
+ "cd /source",
15902
+ "find . -maxdepth 1 ! -name node_modules ! -name . -exec cp -r {} /app/ \\;",
15903
+ "cd /app",
15904
+ // Always install fresh for Linux platform
15905
+ "npm install",
15906
+ // Run the dev server
15907
+ frameworkConfig.command.join(" ")
15908
+ ].join(" && ")
15909
+ ],
15820
15910
  ports: [
15821
15911
  {
15822
15912
  containerPort: options.port,
@@ -15831,16 +15921,21 @@ function generateUIManifests(options) {
15831
15921
  volumeMounts: [
15832
15922
  {
15833
15923
  name: "source",
15924
+ mountPath: "/source",
15925
+ readOnly: true
15926
+ },
15927
+ {
15928
+ name: "workdir",
15834
15929
  mountPath: "/app"
15835
15930
  }
15836
15931
  ],
15837
15932
  workingDir: "/app",
15838
15933
  resources: {
15839
15934
  limits: {
15840
- memory: "512Mi"
15935
+ memory: "1Gi"
15841
15936
  },
15842
15937
  requests: {
15843
- memory: "256Mi"
15938
+ memory: "512Mi"
15844
15939
  }
15845
15940
  }
15846
15941
  }
@@ -15852,6 +15947,10 @@ function generateUIManifests(options) {
15852
15947
  path: options.sourceDir,
15853
15948
  type: "DirectoryOrCreate"
15854
15949
  }
15950
+ },
15951
+ {
15952
+ name: "workdir",
15953
+ emptyDir: {}
15855
15954
  }
15856
15955
  ]
15857
15956
  }
@@ -16568,7 +16667,7 @@ function generateNginxConfig(routes, servicePortMap) {
16568
16667
  const basePath = route.path.replace(/\/?\*$/, "");
16569
16668
  return `
16570
16669
  location ${basePath}/ {
16571
- proxy_pass ${upstream}/;
16670
+ proxy_pass ${upstream};
16572
16671
  proxy_http_version 1.1;
16573
16672
  proxy_set_header Upgrade $http_upgrade;
16574
16673
  proxy_set_header Connection 'upgrade';
@@ -16686,8 +16785,14 @@ function generateK8sManifests(options) {
16686
16785
  const projectName = config.project.name;
16687
16786
  const portAllocator = createPortAllocator();
16688
16787
  const servicePortMap = {};
16788
+ let kernelUrl;
16789
+ if (config.project.kernel) {
16790
+ kernelUrl = `http://${config.project.kernel.name}:8090`;
16791
+ } else if (config.project.gcpKernel) {
16792
+ kernelUrl = `http://${config.project.gcpKernel.name}:8080`;
16793
+ }
16689
16794
  manifests.push(generateNamespace(projectName));
16690
- manifests.push(generateConfigMap({ projectName }));
16795
+ manifests.push(generateConfigMap({ projectName, kernelUrl }));
16691
16796
  if (includeEmulators) {
16692
16797
  manifests.push(generateFirebaseEmulator({ projectName }));
16693
16798
  manifests.push(generatePubSubEmulator({ projectName }));
@@ -18368,8 +18473,10 @@ var unregisterCommand = new Command23("unregister").description("Remove a projec
18368
18473
  });
18369
18474
 
18370
18475
  // src/index.ts
18476
+ var require2 = createRequire(import.meta.url);
18477
+ var pkg = require2("../package.json");
18371
18478
  var program = new Command24();
18372
- program.name("stacksolo").description("Deploy cloud infrastructure for your applications").version("0.1.0");
18479
+ program.name("stacksolo").description("Deploy cloud infrastructure for your applications").version(pkg.version);
18373
18480
  program.addCommand(initCommand);
18374
18481
  program.addCommand(scaffoldCommand);
18375
18482
  program.addCommand(cloneCommand);