@geekmidas/cli 1.4.0 → 1.5.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 (51) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/{Route53Provider-xrWuBXih.cjs → Route53Provider-Bs7Arms9.cjs} +3 -2
  3. package/dist/Route53Provider-Bs7Arms9.cjs.map +1 -0
  4. package/dist/{Route53Provider-DOWmFnwN.mjs → Route53Provider-C8mS0zY6.mjs} +3 -2
  5. package/dist/Route53Provider-C8mS0zY6.mjs.map +1 -0
  6. package/dist/{config-C1bidhvG.mjs → config-DfCJ29PQ.mjs} +2 -2
  7. package/dist/{config-C1bidhvG.mjs.map → config-DfCJ29PQ.mjs.map} +1 -1
  8. package/dist/{config-C1dM7aZb.cjs → config-ZQM1vBoz.cjs} +2 -2
  9. package/dist/{config-C1dM7aZb.cjs.map → config-ZQM1vBoz.cjs.map} +1 -1
  10. package/dist/config.cjs +2 -2
  11. package/dist/config.d.cts +1 -1
  12. package/dist/config.d.mts +1 -1
  13. package/dist/config.mjs +2 -2
  14. package/dist/{index-DzmZ6SUW.d.cts → index-B58qjyBd.d.cts} +27 -1
  15. package/dist/index-B58qjyBd.d.cts.map +1 -0
  16. package/dist/{index-DvpWzLD7.d.mts → index-C0SpUT9Y.d.mts} +27 -1
  17. package/dist/index-C0SpUT9Y.d.mts.map +1 -0
  18. package/dist/index.cjs +105 -48
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.mjs +105 -48
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/{openapi-9k6a6VA4.mjs → openapi-BcSjLfWq.mjs} +2 -2
  23. package/dist/{openapi-9k6a6VA4.mjs.map → openapi-BcSjLfWq.mjs.map} +1 -1
  24. package/dist/{openapi-Dcja4e1C.cjs → openapi-D6Hcfov0.cjs} +2 -2
  25. package/dist/{openapi-Dcja4e1C.cjs.map → openapi-D6Hcfov0.cjs.map} +1 -1
  26. package/dist/openapi.cjs +3 -3
  27. package/dist/openapi.mjs +3 -3
  28. package/dist/workspace/index.cjs +1 -1
  29. package/dist/workspace/index.d.cts +1 -1
  30. package/dist/workspace/index.d.mts +1 -1
  31. package/dist/workspace/index.mjs +1 -1
  32. package/dist/{workspace-CeFgIDC-.cjs → workspace-2Do2YcGZ.cjs} +5 -1
  33. package/dist/{workspace-CeFgIDC-.cjs.map → workspace-2Do2YcGZ.cjs.map} +1 -1
  34. package/dist/{workspace-Cb_I7oCJ.mjs → workspace-BW2iU37P.mjs} +5 -1
  35. package/dist/{workspace-Cb_I7oCJ.mjs.map → workspace-BW2iU37P.mjs.map} +1 -1
  36. package/package.json +4 -4
  37. package/src/deploy/__tests__/Route53Provider.spec.ts +23 -0
  38. package/src/deploy/__tests__/env-resolver.spec.ts +239 -0
  39. package/src/deploy/__tests__/sniffer.spec.ts +104 -93
  40. package/src/deploy/dns/Route53Provider.ts +4 -1
  41. package/src/deploy/env-resolver.ts +11 -1
  42. package/src/deploy/index.ts +72 -24
  43. package/src/deploy/sniffer.ts +39 -7
  44. package/src/init/generators/monorepo.ts +4 -0
  45. package/src/init/generators/web.ts +45 -2
  46. package/src/workspace/schema.ts +8 -0
  47. package/src/workspace/types.ts +23 -0
  48. package/dist/Route53Provider-DOWmFnwN.mjs.map +0 -1
  49. package/dist/Route53Provider-xrWuBXih.cjs.map +0 -1
  50. package/dist/index-DvpWzLD7.d.mts.map +0 -1
  51. package/dist/index-DzmZ6SUW.d.cts.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  const require_chunk = require('./chunk-CUT6urMc.cjs');
3
- const require_workspace = require('./workspace-CeFgIDC-.cjs');
4
- const require_config = require('./config-C1dM7aZb.cjs');
3
+ const require_workspace = require('./workspace-2Do2YcGZ.cjs');
4
+ const require_config = require('./config-ZQM1vBoz.cjs');
5
5
  const require_credentials = require('./credentials-C8DWtnMY.cjs');
6
- const require_openapi = require('./openapi-Dcja4e1C.cjs');
6
+ const require_openapi = require('./openapi-D6Hcfov0.cjs');
7
7
  const require_storage = require('./storage-CoCNe0Pt.cjs');
8
8
  const require_dokploy_api = require('./dokploy-api-CQvhV6Hd.cjs');
9
9
  const require_encryption = require('./encryption-BE0UOb8j.cjs');
@@ -32,7 +32,7 @@ const prompts = require_chunk.__toESM(require("prompts"));
32
32
 
33
33
  //#region package.json
34
34
  var name = "@geekmidas/cli";
35
- var version = "1.3.0";
35
+ var version = "1.4.0";
36
36
  var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
37
37
  var private$1 = false;
38
38
  var type = "module";
@@ -2203,7 +2203,7 @@ async function createDnsProvider(options) {
2203
2203
  return new HostingerProvider();
2204
2204
  }
2205
2205
  if (provider === "route53") {
2206
- const { Route53Provider } = await Promise.resolve().then(() => require("./Route53Provider-xrWuBXih.cjs"));
2206
+ const { Route53Provider } = await Promise.resolve().then(() => require("./Route53Provider-Bs7Arms9.cjs"));
2207
2207
  const route53Config = config;
2208
2208
  return new Route53Provider({
2209
2209
  region: route53Config.region,
@@ -4137,33 +4137,6 @@ function isMainFrontendApp(appName, app, allApps) {
4137
4137
  for (const [name$1, a] of Object.entries(allApps)) if (a.type === "frontend") return name$1 === appName;
4138
4138
  return false;
4139
4139
  }
4140
- /**
4141
- * Generate public URL build args for a frontend app based on its dependencies.
4142
- *
4143
- * @param app - The frontend app configuration
4144
- * @param deployedUrls - Map of app name to deployed public URL
4145
- * @returns Array of build args like 'NEXT_PUBLIC_API_URL=https://api.example.com'
4146
- */
4147
- function generatePublicUrlBuildArgs(app, deployedUrls) {
4148
- const buildArgs = [];
4149
- for (const dep of app.dependencies) {
4150
- const publicUrl = deployedUrls[dep];
4151
- if (publicUrl) {
4152
- const envVarName = `NEXT_PUBLIC_${dep.toUpperCase()}_URL`;
4153
- buildArgs.push(`${envVarName}=${publicUrl}`);
4154
- }
4155
- }
4156
- return buildArgs;
4157
- }
4158
- /**
4159
- * Get public URL arg names from app dependencies.
4160
- *
4161
- * @param app - The frontend app configuration
4162
- * @returns Array of arg names like 'NEXT_PUBLIC_API_URL'
4163
- */
4164
- function getPublicUrlArgNames(app) {
4165
- return app.dependencies.map((dep) => `NEXT_PUBLIC_${dep.toUpperCase()}_URL`);
4166
- }
4167
4140
 
4168
4141
  //#endregion
4169
4142
  //#region src/deploy/env-resolver.ts
@@ -4225,7 +4198,9 @@ function resolveEnvVar(varName, context) {
4225
4198
  break;
4226
4199
  }
4227
4200
  if (context.dependencyUrls && varName.endsWith("_URL")) {
4228
- const depName = varName.slice(0, -4).toLowerCase();
4201
+ let depName;
4202
+ if (varName.startsWith("NEXT_PUBLIC_")) depName = varName.slice(12, -4).toLowerCase();
4203
+ else depName = varName.slice(0, -4).toLowerCase();
4229
4204
  if (context.dependencyUrls[depName]) return context.dependencyUrls[depName];
4230
4205
  }
4231
4206
  if (context.userSecrets) {
@@ -4637,14 +4612,29 @@ function resolveSnifferFile(baseName) {
4637
4612
  */
4638
4613
  async function sniffAppEnvironment(app, appName, workspacePath, options = {}) {
4639
4614
  const { logWarnings = true } = options;
4640
- if (app.type === "frontend") return {
4641
- appName,
4642
- requiredEnvVars: []
4643
- };
4644
- if (app.requiredEnv && app.requiredEnv.length > 0) return {
4645
- appName,
4646
- requiredEnvVars: [...app.requiredEnv]
4647
- };
4615
+ if (app.type === "frontend") {
4616
+ const depVars = (app.dependencies ?? []).map((dep) => `NEXT_PUBLIC_${dep.toUpperCase()}_URL`);
4617
+ if (app.config) {
4618
+ const sniffedVars = [];
4619
+ const configPaths = [];
4620
+ if (app.config.client) configPaths.push(app.config.client);
4621
+ if (app.config.server) configPaths.push(app.config.server);
4622
+ for (const configPath of configPaths) {
4623
+ const result = await sniffEntryFile(configPath, app.path, workspacePath);
4624
+ if (logWarnings && result.error) console.warn(`[sniffer] ${appName}: Config file "${configPath}" threw error during sniffing (env vars still captured): ${result.error.message}`);
4625
+ sniffedVars.push(...result.envVars);
4626
+ }
4627
+ const allVars = [...new Set([...depVars, ...sniffedVars])];
4628
+ return {
4629
+ appName,
4630
+ requiredEnvVars: allVars
4631
+ };
4632
+ }
4633
+ return {
4634
+ appName,
4635
+ requiredEnvVars: depVars
4636
+ };
4637
+ }
4648
4638
  if (app.entry) {
4649
4639
  const result = await sniffEntryFile(app.entry, app.path, workspacePath);
4650
4640
  if (logWarnings && result.error) console.warn(`[sniffer] ${appName}: Entry file threw error during sniffing (env vars still captured): ${result.error.message}`);
@@ -5710,8 +5700,33 @@ async function workspaceDeployCommand(workspace, options) {
5710
5700
  else logger$1.log(` Found existing application: ${application.applicationId}`);
5711
5701
  }
5712
5702
  setApplicationId(state, appName, application.applicationId);
5713
- const buildArgs = generatePublicUrlBuildArgs(app, publicUrls);
5714
- if (buildArgs.length > 0) logger$1.log(` Public URLs: ${buildArgs.join(", ")}`);
5703
+ const dependencyUrls = {};
5704
+ if (app.dependencies) {
5705
+ for (const dep of app.dependencies) if (publicUrls[dep]) dependencyUrls[dep] = publicUrls[dep];
5706
+ }
5707
+ const isMainFrontend = isMainFrontendApp(appName, app, workspace.apps);
5708
+ const frontendHost = resolveHost(appName, app, stage, dokployConfig, isMainFrontend);
5709
+ const envContext = {
5710
+ app,
5711
+ appName,
5712
+ stage,
5713
+ state,
5714
+ appHostname: frontendHost,
5715
+ frontendUrls: [],
5716
+ userSecrets: stageSecrets ?? void 0,
5717
+ dependencyUrls
5718
+ };
5719
+ const sniffedVars = sniffedApps.get(appName)?.requiredEnvVars ?? [];
5720
+ const { valid, missing, resolved } = validateEnvVars(sniffedVars, envContext);
5721
+ if (!valid) throw new Error(formatMissingVarsError(appName, missing, stage));
5722
+ if (Object.keys(resolved).length > 0) logger$1.log(` Resolved ${Object.keys(resolved).length} env vars: ${Object.keys(resolved).join(", ")}`);
5723
+ const buildArgs = [];
5724
+ const publicUrlArgNames = [];
5725
+ for (const [key, value] of Object.entries(resolved)) if (key.startsWith("NEXT_PUBLIC_")) {
5726
+ buildArgs.push(`${key}=${value}`);
5727
+ publicUrlArgNames.push(key);
5728
+ }
5729
+ if (buildArgs.length > 0) logger$1.log(` Build args: ${publicUrlArgNames.join(", ")}`);
5715
5730
  const imageName = `${workspace.name}-${appName}`;
5716
5731
  const imageRef = registry ? `${registry}/${imageName}:${imageTag}` : `${imageName}:${imageTag}`;
5717
5732
  logger$1.log(` Building Docker image: ${imageRef}`);
@@ -5725,19 +5740,18 @@ async function workspaceDeployCommand(workspace, options) {
5725
5740
  appName
5726
5741
  },
5727
5742
  buildArgs,
5728
- publicUrlArgs: getPublicUrlArgNames(app)
5743
+ publicUrlArgs: publicUrlArgNames
5729
5744
  });
5730
5745
  const envVars = [
5731
5746
  `NODE_ENV=production`,
5732
5747
  `PORT=${app.port}`,
5733
5748
  `STAGE=${stage}`
5734
5749
  ];
5750
+ for (const [key, value] of Object.entries(resolved)) envVars.push(`${key}=${value}`);
5735
5751
  await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
5736
5752
  await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
5737
5753
  logger$1.log(` Deploying to Dokploy...`);
5738
5754
  await api.deployApplication(application.applicationId);
5739
- const isMainFrontend = isMainFrontendApp(appName, app, workspace.apps);
5740
- const frontendHost = resolveHost(appName, app, stage, dokployConfig, isMainFrontend);
5741
5755
  const existingFrontendDomains = await api.getDomainsByApplicationId(application.applicationId);
5742
5756
  const existingFrontendDomain = existingFrontendDomains.find((d) => d.host === frontendHost);
5743
5757
  if (existingFrontendDomain) {
@@ -7365,6 +7379,10 @@ export default defineWorkspace({
7365
7379
  path: 'apps/web',
7366
7380
  port: 3001,
7367
7381
  dependencies: ['api', 'auth'],
7382
+ config: {
7383
+ client: './src/config/client.ts',
7384
+ server: './src/config/server.ts',
7385
+ },
7368
7386
  client: {
7369
7387
  output: './src/api',
7370
7388
  },
@@ -9959,12 +9977,42 @@ export function getQueryClient() {
9959
9977
  if (!browserQueryClient) browserQueryClient = makeQueryClient();
9960
9978
  return browserQueryClient;
9961
9979
  }
9980
+ `;
9981
+ const clientConfigTs = `import { EnvironmentParser } from '@geekmidas/envkit';
9982
+
9983
+ // Client config - only NEXT_PUBLIC_* vars (available in browser)
9984
+ // These values are inlined at build time by Next.js
9985
+ const envParser = new EnvironmentParser({
9986
+ NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
9987
+ NEXT_PUBLIC_AUTH_URL: process.env.NEXT_PUBLIC_AUTH_URL,
9988
+ });
9989
+
9990
+ export const clientConfig = envParser
9991
+ .create((get) => ({
9992
+ apiUrl: get('NEXT_PUBLIC_API_URL').string(),
9993
+ authUrl: get('NEXT_PUBLIC_AUTH_URL').string(),
9994
+ }))
9995
+ .parse();
9996
+ `;
9997
+ const serverConfigTs = `import { EnvironmentParser } from '@geekmidas/envkit';
9998
+
9999
+ // Server config - all env vars (server-side only, not exposed to browser)
10000
+ // Access these only in Server Components, Route Handlers, or Server Actions
10001
+ const envParser = new EnvironmentParser({ ...process.env });
10002
+
10003
+ export const serverConfig = envParser
10004
+ .create((get) => ({
10005
+ // Add server-only secrets here
10006
+ // Example: stripeSecretKey: get('STRIPE_SECRET_KEY').string(),
10007
+ }))
10008
+ .parse();
9962
10009
  `;
9963
10010
  const authClientTs = `import { createAuthClient } from 'better-auth/react';
9964
10011
  import { magicLinkClient } from 'better-auth/client/plugins';
10012
+ import { clientConfig } from '~/config/client';
9965
10013
 
9966
10014
  export const authClient = createAuthClient({
9967
- baseURL: process.env.NEXT_PUBLIC_AUTH_URL || 'http://localhost:3002',
10015
+ baseURL: clientConfig.authUrl,
9968
10016
  plugins: [magicLinkClient()],
9969
10017
  });
9970
10018
 
@@ -9985,9 +10033,10 @@ export function Providers({ children }: { children: React.ReactNode }) {
9985
10033
  `;
9986
10034
  const apiIndexTs = `import { createApi } from './openapi';
9987
10035
  import { getQueryClient } from '~/lib/query-client';
10036
+ import { clientConfig } from '~/config/client';
9988
10037
 
9989
10038
  export const api = createApi({
9990
- baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000',
10039
+ baseURL: clientConfig.apiUrl,
9991
10040
  queryClient: getQueryClient(),
9992
10041
  });
9993
10042
  `;
@@ -10108,6 +10157,14 @@ node_modules/
10108
10157
  path: "apps/web/src/app/page.tsx",
10109
10158
  content: pageTsx
10110
10159
  },
10160
+ {
10161
+ path: "apps/web/src/config/client.ts",
10162
+ content: clientConfigTs
10163
+ },
10164
+ {
10165
+ path: "apps/web/src/config/server.ts",
10166
+ content: serverConfigTs
10167
+ },
10111
10168
  {
10112
10169
  path: "apps/web/src/lib/query-client.ts",
10113
10170
  content: queryClientTs