@geekmidas/cli 0.52.0 → 0.54.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.cjs CHANGED
@@ -20,6 +20,7 @@ const chokidar = require_chunk.__toESM(require("chokidar"));
20
20
  const dotenv = require_chunk.__toESM(require("dotenv"));
21
21
  const fast_glob = require_chunk.__toESM(require("fast-glob"));
22
22
  const __geekmidas_constructs_crons = require_chunk.__toESM(require("@geekmidas/constructs/crons"));
23
+ const __geekmidas_constructs_endpoints = require_chunk.__toESM(require("@geekmidas/constructs/endpoints"));
23
24
  const __geekmidas_constructs_functions = require_chunk.__toESM(require("@geekmidas/constructs/functions"));
24
25
  const __geekmidas_constructs_subscribers = require_chunk.__toESM(require("@geekmidas/constructs/subscribers"));
25
26
  const node_crypto = require_chunk.__toESM(require("node:crypto"));
@@ -31,7 +32,7 @@ const node_module = require_chunk.__toESM(require("node:module"));
31
32
 
32
33
  //#region package.json
33
34
  var name = "@geekmidas/cli";
34
- var version = "0.52.0";
35
+ var version = "0.54.0";
35
36
  var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
36
37
  var private$1 = false;
37
38
  var type = "module";
@@ -3386,7 +3387,7 @@ WORKDIR /app
3386
3387
  COPY . .
3387
3388
 
3388
3389
  # Build production server using gkm
3389
- RUN pnpm exec gkm build --provider server --production
3390
+ RUN ${pm.exec} gkm build --provider server --production
3390
3391
 
3391
3392
  # Stage 3: Production
3392
3393
  FROM ${baseImage} AS runner
@@ -3468,7 +3469,7 @@ WORKDIR /app
3468
3469
  COPY --from=pruner /app/out/full/ ./
3469
3470
 
3470
3471
  # Build production server using gkm
3471
- RUN pnpm exec gkm build --provider server --production
3472
+ RUN ${pm.exec} gkm build --provider server --production
3472
3473
 
3473
3474
  # Stage 4: Production
3474
3475
  FROM ${baseImage} AS runner
@@ -3789,7 +3790,7 @@ RUN if [ -n "$GKM_ENCRYPTED_CREDENTIALS" ]; then \
3789
3790
  fi
3790
3791
 
3791
3792
  # Build production server using gkm
3792
- RUN cd ${appPath} && pnpm exec gkm build --provider server --production
3793
+ RUN cd ${appPath} && ${pm.exec} gkm build --provider server --production
3793
3794
 
3794
3795
  # Stage 4: Production
3795
3796
  FROM ${baseImage} AS runner
@@ -4523,7 +4524,8 @@ function buildRedisUrl(redis) {
4523
4524
  function resolveEnvVar(varName, context) {
4524
4525
  switch (varName) {
4525
4526
  case "PORT": return String(context.app.port);
4526
- case "NODE_ENV": return context.stage === "production" ? "production" : "development";
4527
+ case "NODE_ENV": return "production";
4528
+ case "STAGE": return context.stage;
4527
4529
  case "DATABASE_URL":
4528
4530
  if (context.appCredentials && context.postgres) return buildDatabaseUrl(context.appCredentials, context.postgres);
4529
4531
  break;
@@ -4857,6 +4859,12 @@ function generateSecretsReport(encryptedApps, sniffedApps) {
4857
4859
  const __filename$1 = (0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href);
4858
4860
  const __dirname$1 = (0, node_path.dirname)(__filename$1);
4859
4861
  /**
4862
+ * Check if a value is a gkm construct (Endpoint, Function, Cron, or Subscriber).
4863
+ */
4864
+ function isConstruct(value) {
4865
+ return __geekmidas_constructs_endpoints.Endpoint.isEndpoint(value) || __geekmidas_constructs_functions.Function.isFunction(value) || __geekmidas_constructs_crons.Cron.isCron(value) || __geekmidas_constructs_subscribers.Subscriber.isSubscriber(value);
4866
+ }
4867
+ /**
4860
4868
  * Resolve the path to a sniffer helper file.
4861
4869
  * Handles both dev (.ts with tsx) and production (.mjs from dist).
4862
4870
  *
@@ -4881,8 +4889,9 @@ function resolveSnifferFile(baseName) {
4881
4889
  * 1. Frontend apps: Returns empty (no server secrets)
4882
4890
  * 2. Apps with `requiredEnv`: Uses explicit list from config
4883
4891
  * 3. Entry apps: Imports entry file in subprocess to capture config.parse() calls
4884
- * 4. Apps with `envParser`: Runs SnifferEnvironmentParser to detect usage
4885
- * 5. Apps with neither: Returns empty
4892
+ * 4. Route-based apps: Loads route files and calls getEnvironment() on each construct
4893
+ * 5. Apps with `envParser` (no routes): Runs SnifferEnvironmentParser to detect usage
4894
+ * 6. Apps with neither: Returns empty
4886
4895
  *
4887
4896
  * This function handles "fire and forget" async operations gracefully,
4888
4897
  * capturing errors and unhandled rejections without failing the build.
@@ -4911,6 +4920,14 @@ async function sniffAppEnvironment(app, appName, workspacePath, options = {}) {
4911
4920
  requiredEnvVars: result.envVars
4912
4921
  };
4913
4922
  }
4923
+ if (app.routes) {
4924
+ const result = await sniffRouteFiles(app.routes, app.path, workspacePath);
4925
+ if (logWarnings && result.error) console.warn(`[sniffer] ${appName}: Route sniffing threw error (env vars still captured): ${result.error.message}`);
4926
+ return {
4927
+ appName,
4928
+ requiredEnvVars: result.envVars
4929
+ };
4930
+ }
4914
4931
  if (app.envParser) {
4915
4932
  const result = await sniffEnvParser(app.envParser, app.path, workspacePath);
4916
4933
  if (logWarnings) {
@@ -5002,6 +5019,47 @@ async function sniffEntryFile(entryPath, appPath, workspacePath) {
5002
5019
  });
5003
5020
  }
5004
5021
  /**
5022
+ * Sniff route files by loading constructs and calling getEnvironment().
5023
+ *
5024
+ * Route-based apps have endpoints, functions, crons, and subscribers that
5025
+ * use services. Each service's register() method accesses environment variables.
5026
+ * This function mimics what the bundler does during build to capture those vars.
5027
+ *
5028
+ * @param routes - Glob pattern(s) for route files
5029
+ * @param appPath - The app's path relative to workspace (e.g., 'apps/api')
5030
+ * @param workspacePath - Absolute path to workspace root
5031
+ * @returns EntrySniffResult with env vars and optional error
5032
+ */
5033
+ async function sniffRouteFiles(routes, appPath, workspacePath) {
5034
+ const fullAppPath = (0, node_path.resolve)(workspacePath, appPath);
5035
+ const patterns = Array.isArray(routes) ? routes : [routes];
5036
+ const envVars = /* @__PURE__ */ new Set();
5037
+ let error;
5038
+ try {
5039
+ const files = await (0, fast_glob.default)(patterns, {
5040
+ cwd: fullAppPath,
5041
+ absolute: true
5042
+ });
5043
+ for (const file of files) try {
5044
+ const module$1 = await import(file);
5045
+ for (const [, exportValue] of Object.entries(module$1)) if (isConstruct(exportValue)) try {
5046
+ const constructEnvVars = await exportValue.getEnvironment();
5047
+ constructEnvVars.forEach((v) => envVars.add(v));
5048
+ } catch (e) {
5049
+ console.warn(`[sniffer] Failed to get environment for construct in ${file}: ${e instanceof Error ? e.message : String(e)}`);
5050
+ }
5051
+ } catch (e) {
5052
+ console.warn(`[sniffer] Failed to import ${file}: ${e instanceof Error ? e.message : String(e)}`);
5053
+ }
5054
+ } catch (e) {
5055
+ error = e instanceof Error ? e : new Error(String(e));
5056
+ }
5057
+ return {
5058
+ envVars: Array.from(envVars).sort(),
5059
+ error
5060
+ };
5061
+ }
5062
+ /**
5005
5063
  * Run the SnifferEnvironmentParser on an envParser module to detect
5006
5064
  * which environment variables it accesses.
5007
5065
  *
@@ -5789,7 +5847,13 @@ async function workspaceDeployCommand(workspace, options) {
5789
5847
  masterKey: appSecrets?.masterKey
5790
5848
  };
5791
5849
  const appRequirements = sniffedApps.get(appName);
5792
- const requiredVars = appRequirements?.requiredEnvVars ?? [];
5850
+ const sniffedVars = appRequirements?.requiredEnvVars ?? [];
5851
+ const requiredVars = [...new Set([
5852
+ "PORT",
5853
+ "NODE_ENV",
5854
+ "STAGE",
5855
+ ...sniffedVars
5856
+ ])];
5793
5857
  const { valid, missing, resolved } = validateEnvVars(requiredVars, envContext);
5794
5858
  if (!valid) throw new Error(formatMissingVarsError(appName, missing, stage));
5795
5859
  const envVars = Object.entries(resolved).map(([key, value]) => `${key}=${value}`);
@@ -5883,7 +5947,11 @@ async function workspaceDeployCommand(workspace, options) {
5883
5947
  buildArgs,
5884
5948
  publicUrlArgs: getPublicUrlArgNames(app)
5885
5949
  });
5886
- const envVars = [`NODE_ENV=production`, `PORT=${app.port}`];
5950
+ const envVars = [
5951
+ `NODE_ENV=production`,
5952
+ `PORT=${app.port}`,
5953
+ `STAGE=${stage}`
5954
+ ];
5887
5955
  await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
5888
5956
  await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
5889
5957
  logger$1.log(` Deploying to Dokploy...`);