@geekmidas/cli 0.22.0 → 0.24.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/src/dev/index.ts CHANGED
@@ -14,7 +14,12 @@ import type {
14
14
  NormalizedStudioConfig,
15
15
  NormalizedTelescopeConfig,
16
16
  } from '../build/types';
17
- import { loadWorkspaceConfig, parseModuleConfig } from '../config';
17
+ import {
18
+ getAppNameFromCwd,
19
+ loadAppConfig,
20
+ loadWorkspaceConfig,
21
+ parseModuleConfig,
22
+ } from '../config';
18
23
  import {
19
24
  CronGenerator,
20
25
  EndpointGenerator,
@@ -230,6 +235,7 @@ export function normalizeStudioConfig(
230
235
  */
231
236
  export function normalizeHooksConfig(
232
237
  config: GkmConfig['hooks'],
238
+ cwd: string = process.cwd(),
233
239
  ): NormalizedHooksConfig | undefined {
234
240
  if (!config?.server) {
235
241
  return undefined;
@@ -240,7 +246,7 @@ export function normalizeHooksConfig(
240
246
  ? config.server
241
247
  : `${config.server}.ts`;
242
248
 
243
- const resolvedPath = resolve(process.cwd(), serverPath);
249
+ const resolvedPath = resolve(cwd, serverPath);
244
250
 
245
251
  return {
246
252
  serverHooksPath: resolvedPath,
@@ -308,21 +314,47 @@ export async function devCommand(options: DevOptions): Promise<void> {
308
314
  logger.log(`📦 Loaded env: ${defaultEnv.loaded.join(', ')}`);
309
315
  }
310
316
 
311
- // Try to load workspace config first
312
- const loadedConfig = await loadWorkspaceConfig();
317
+ // Check if we're in an app subdirectory
318
+ const appName = getAppNameFromCwd();
319
+ let config: GkmConfig;
320
+ let appRoot: string = process.cwd();
313
321
 
314
- // Route to workspace dev mode for multi-app workspaces
315
- if (loadedConfig.type === 'workspace') {
316
- logger.log('📦 Detected workspace configuration');
317
- return workspaceDevCommand(loadedConfig.workspace, options);
318
- }
322
+ if (appName) {
323
+ // Try to load app-specific config from workspace
324
+ try {
325
+ const appConfig = await loadAppConfig();
326
+ config = appConfig.gkmConfig;
327
+ appRoot = appConfig.appRoot;
328
+ logger.log(`📦 Running app: ${appConfig.appName}`);
329
+ } catch {
330
+ // Not in a workspace or app not found in workspace - fall back to regular loading
331
+ const loadedConfig = await loadWorkspaceConfig();
332
+
333
+ // Route to workspace dev mode for multi-app workspaces
334
+ if (loadedConfig.type === 'workspace') {
335
+ logger.log('📦 Detected workspace configuration');
336
+ return workspaceDevCommand(loadedConfig.workspace, options);
337
+ }
338
+
339
+ config = loadedConfig.raw as GkmConfig;
340
+ }
341
+ } else {
342
+ // Try to load workspace config
343
+ const loadedConfig = await loadWorkspaceConfig();
344
+
345
+ // Route to workspace dev mode for multi-app workspaces
346
+ if (loadedConfig.type === 'workspace') {
347
+ logger.log('📦 Detected workspace configuration');
348
+ return workspaceDevCommand(loadedConfig.workspace, options);
349
+ }
319
350
 
320
- // Single-app mode - use existing logic
321
- const config = loadedConfig.raw as GkmConfig;
351
+ // Single-app mode - use existing logic
352
+ config = loadedConfig.raw as GkmConfig;
353
+ }
322
354
 
323
355
  // Load any additional env files specified in config
324
356
  if (config.env) {
325
- const { loaded, missing } = loadEnvFiles(config.env);
357
+ const { loaded, missing } = loadEnvFiles(config.env, appRoot);
326
358
  if (loaded.length > 0) {
327
359
  logger.log(`📦 Loaded env: ${loaded.join(', ')}`);
328
360
  }
@@ -366,7 +398,7 @@ export async function devCommand(options: DevOptions): Promise<void> {
366
398
  }
367
399
 
368
400
  // Normalize hooks configuration
369
- const hooks = normalizeHooksConfig(config.hooks);
401
+ const hooks = normalizeHooksConfig(config.hooks, appRoot);
370
402
  if (hooks) {
371
403
  logger.log(`🪝 Server hooks enabled from ${config.hooks?.server}`);
372
404
  }
@@ -395,6 +427,7 @@ export async function devCommand(options: DevOptions): Promise<void> {
395
427
  buildContext,
396
428
  resolved.providers[0] as LegacyProvider,
397
429
  enableOpenApi,
430
+ appRoot,
398
431
  );
399
432
 
400
433
  // Generate OpenAPI spec on startup
@@ -414,6 +447,7 @@ export async function devCommand(options: DevOptions): Promise<void> {
414
447
  telescope,
415
448
  studio,
416
449
  runtime,
450
+ appRoot,
417
451
  );
418
452
 
419
453
  await devServer.start();
@@ -451,7 +485,7 @@ export async function devCommand(options: DevOptions): Promise<void> {
451
485
 
452
486
  // Resolve glob patterns to actual files (chokidar 4.x doesn't support globs)
453
487
  const resolvedFiles = await fg(normalizedPatterns, {
454
- cwd: process.cwd(),
488
+ cwd: appRoot,
455
489
  absolute: false,
456
490
  onlyFiles: true,
457
491
  });
@@ -474,7 +508,7 @@ export async function devCommand(options: DevOptions): Promise<void> {
474
508
  ignored: /(^|[/\\])\../, // ignore dotfiles
475
509
  persistent: true,
476
510
  ignoreInitial: true,
477
- cwd: process.cwd(),
511
+ cwd: appRoot,
478
512
  });
479
513
 
480
514
  watcher.on('ready', () => {
@@ -503,6 +537,7 @@ export async function devCommand(options: DevOptions): Promise<void> {
503
537
  buildContext,
504
538
  resolved.providers[0] as LegacyProvider,
505
539
  enableOpenApi,
540
+ appRoot,
506
541
  );
507
542
 
508
543
  // Regenerate OpenAPI if enabled
@@ -903,6 +938,17 @@ async function workspaceDevCommand(
903
938
  );
904
939
  }
905
940
 
941
+ // Find the config file path for GKM_CONFIG_PATH
942
+ const configFiles = ['gkm.config.ts', 'gkm.config.js', 'gkm.config.json'];
943
+ let configPath = '';
944
+ for (const file of configFiles) {
945
+ const fullPath = join(workspace.root, file);
946
+ if (existsSync(fullPath)) {
947
+ configPath = fullPath;
948
+ break;
949
+ }
950
+ }
951
+
906
952
  // Prepare environment variables
907
953
  // Order matters: secrets first, then dependencies (dependencies can override)
908
954
  const turboEnv: Record<string, string> = {
@@ -910,6 +956,8 @@ async function workspaceDevCommand(
910
956
  ...secretsEnv,
911
957
  ...dependencyEnv,
912
958
  NODE_ENV: 'development',
959
+ // Inject config path so child processes can find the workspace config
960
+ ...(configPath ? { GKM_CONFIG_PATH: configPath } : {}),
913
961
  };
914
962
 
915
963
  // Spawn turbo run dev
@@ -1095,6 +1143,7 @@ async function buildServer(
1095
1143
  context: BuildContext,
1096
1144
  provider: LegacyProvider,
1097
1145
  enableOpenApi: boolean,
1146
+ appRoot: string = process.cwd(),
1098
1147
  ): Promise<void> {
1099
1148
  // Initialize generators
1100
1149
  const endpointGenerator = new EndpointGenerator();
@@ -1102,17 +1151,19 @@ async function buildServer(
1102
1151
  const cronGenerator = new CronGenerator();
1103
1152
  const subscriberGenerator = new SubscriberGenerator();
1104
1153
 
1105
- // Load all constructs
1154
+ // Load all constructs (resolve paths relative to appRoot)
1106
1155
  const [allEndpoints, allFunctions, allCrons, allSubscribers] =
1107
1156
  await Promise.all([
1108
- endpointGenerator.load(config.routes),
1109
- config.functions ? functionGenerator.load(config.functions) : [],
1110
- config.crons ? cronGenerator.load(config.crons) : [],
1111
- config.subscribers ? subscriberGenerator.load(config.subscribers) : [],
1157
+ endpointGenerator.load(config.routes, appRoot),
1158
+ config.functions ? functionGenerator.load(config.functions, appRoot) : [],
1159
+ config.crons ? cronGenerator.load(config.crons, appRoot) : [],
1160
+ config.subscribers
1161
+ ? subscriberGenerator.load(config.subscribers, appRoot)
1162
+ : [],
1112
1163
  ]);
1113
1164
 
1114
- // Ensure .gkm directory exists
1115
- const outputDir = join(process.cwd(), '.gkm', provider);
1165
+ // Ensure .gkm directory exists in app root
1166
+ const outputDir = join(appRoot, '.gkm', provider);
1116
1167
  await mkdir(outputDir, { recursive: true });
1117
1168
 
1118
1169
  // Build for server provider
@@ -1137,9 +1188,10 @@ class DevServer {
1137
1188
  private requestedPort: number,
1138
1189
  private portExplicit: boolean,
1139
1190
  private enableOpenApi: boolean,
1140
- private telescope?: NormalizedTelescopeConfig,
1141
- private studio?: NormalizedStudioConfig,
1191
+ private telescope: NormalizedTelescopeConfig | undefined,
1192
+ private studio: NormalizedStudioConfig | undefined,
1142
1193
  private runtime: Runtime = 'node',
1194
+ private appRoot: string = process.cwd(),
1143
1195
  ) {
1144
1196
  this.actualPort = requestedPort;
1145
1197
  }
@@ -1172,7 +1224,7 @@ class DevServer {
1172
1224
  }
1173
1225
 
1174
1226
  const serverEntryPath = join(
1175
- process.cwd(),
1227
+ this.appRoot,
1176
1228
  '.gkm',
1177
1229
  this.provider,
1178
1230
  'server.ts',
@@ -1292,7 +1344,7 @@ class DevServer {
1292
1344
  const { writeFile } = await import('node:fs/promises');
1293
1345
  const { relative, dirname } = await import('node:path');
1294
1346
 
1295
- const serverPath = join(process.cwd(), '.gkm', this.provider, 'server.ts');
1347
+ const serverPath = join(this.appRoot, '.gkm', this.provider, 'server.ts');
1296
1348
 
1297
1349
  const relativeAppPath = relative(
1298
1350
  dirname(serverPath),
@@ -147,7 +147,7 @@ export function generateDockerFiles(
147
147
  }
148
148
 
149
149
  // Mailpit for email testing
150
- if (options.services.mail) {
150
+ if (options.services?.mail) {
151
151
  services.push(` mailpit:
152
152
  image: axllent/mailpit:latest
153
153
  container_name: ${options.name}-mailpit
@@ -1,8 +1,19 @@
1
1
  import { createRequire } from 'node:module';
2
2
 
3
3
  const require = createRequire(import.meta.url);
4
- // Path is ../package.json from dist/ (bundled output is flat)
5
- const pkg = require('../package.json') as { version: string };
4
+
5
+ // Load package.json - handles both bundled (flat dist/) and source (nested src/init/)
6
+ function loadPackageJson(): { version: string } {
7
+ try {
8
+ // Try flat dist path first (../package.json from dist/)
9
+ return require('../package.json');
10
+ } catch {
11
+ // Fall back to nested source path (../../package.json from src/init/)
12
+ return require('../../package.json');
13
+ }
14
+ }
15
+
16
+ const pkg = loadPackageJson();
6
17
 
7
18
  /**
8
19
  * CLI version from package.json (used for scaffolded projects)
@@ -1,115 +0,0 @@
1
- import { isWorkspaceConfig, processConfig } from "./workspace-CPLEZDZf.mjs";
2
- import { existsSync } from "node:fs";
3
- import { join } from "node:path";
4
-
5
- //#region src/config.ts
6
- /**
7
- * Define GKM configuration with full TypeScript support.
8
- * This is an identity function that provides type safety and autocomplete.
9
- *
10
- * @example
11
- * ```ts
12
- * // gkm.config.ts
13
- * import { defineConfig } from '@geekmidas/cli/config';
14
- *
15
- * export default defineConfig({
16
- * routes: './src/endpoints/**\/*.ts',
17
- * envParser: './src/config/env',
18
- * logger: './src/config/logger',
19
- * telescope: true,
20
- * });
21
- * ```
22
- */
23
- function defineConfig(config) {
24
- return config;
25
- }
26
- /**
27
- * Parse a module config string into path and import pattern.
28
- *
29
- * @param configString - Config string in format "./path/to/module" or "./path/to/module#exportName"
30
- * @param defaultAlias - The default alias name to use if no export name specified
31
- * @returns Object with path and import pattern
32
- *
33
- * @example
34
- * parseModuleConfig('./src/config/env', 'envParser')
35
- * // { path: './src/config/env', importPattern: 'envParser' }
36
- *
37
- * parseModuleConfig('./src/config/env#envParser', 'envParser')
38
- * // { path: './src/config/env', importPattern: '{ envParser }' }
39
- *
40
- * parseModuleConfig('./src/config/env#myEnv', 'envParser')
41
- * // { path: './src/config/env', importPattern: '{ myEnv as envParser }' }
42
- */
43
- function parseModuleConfig(configString, defaultAlias) {
44
- const parts = configString.split("#");
45
- const path = parts[0] ?? configString;
46
- const exportName = parts[1];
47
- const importPattern = !exportName ? defaultAlias : exportName === defaultAlias ? `{ ${defaultAlias} }` : `{ ${exportName} as ${defaultAlias} }`;
48
- return {
49
- path,
50
- importPattern
51
- };
52
- }
53
- /**
54
- * Find and return the path to the config file.
55
- */
56
- function findConfigPath(cwd) {
57
- const files = [
58
- "gkm.config.json",
59
- "gkm.config.ts",
60
- "gkm.config.js"
61
- ];
62
- for (const file of files) {
63
- const path = join(cwd, file);
64
- if (existsSync(path)) return path;
65
- }
66
- throw new Error("Configuration file not found. Please create gkm.config.json, gkm.config.ts, or gkm.config.js in the project root.");
67
- }
68
- /**
69
- * Load raw configuration from file.
70
- */
71
- async function loadRawConfig(cwd) {
72
- const configPath = findConfigPath(cwd);
73
- try {
74
- const config = await import(configPath);
75
- return config.default;
76
- } catch (error) {
77
- throw new Error(`Failed to load config: ${error.message}`);
78
- }
79
- }
80
- /**
81
- * Load configuration file (single-app format).
82
- * For backwards compatibility with existing code.
83
- *
84
- * @deprecated Use loadWorkspaceConfig for new code
85
- */
86
- async function loadConfig(cwd = process.cwd()) {
87
- const config = await loadRawConfig(cwd);
88
- if (isWorkspaceConfig(config)) throw new Error("Workspace configuration detected. Use loadWorkspaceConfig() instead.");
89
- return config;
90
- }
91
- /**
92
- * Load configuration file and process it as a workspace.
93
- * Works with both single-app and workspace configurations.
94
- *
95
- * Single-app configs are automatically wrapped as a workspace with one app.
96
- *
97
- * @example
98
- * ```ts
99
- * const { type, workspace } = await loadWorkspaceConfig();
100
- *
101
- * if (type === 'workspace') {
102
- * console.log('Multi-app workspace:', workspace.apps);
103
- * } else {
104
- * console.log('Single app wrapped as workspace');
105
- * }
106
- * ```
107
- */
108
- async function loadWorkspaceConfig(cwd = process.cwd()) {
109
- const config = await loadRawConfig(cwd);
110
- return processConfig(config, cwd);
111
- }
112
-
113
- //#endregion
114
- export { defineConfig, loadConfig, loadWorkspaceConfig, parseModuleConfig };
115
- //# sourceMappingURL=config-BaYqrF3n.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config-BaYqrF3n.mjs","names":["config: GkmConfig","configString: string","defaultAlias: string","cwd: string"],"sources":["../src/config.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { GkmConfig } from './types.js';\nimport {\n\tisWorkspaceConfig,\n\ttype LoadedConfig,\n\tprocessConfig,\n\ttype WorkspaceConfig,\n} from './workspace/index.js';\n\nexport type { GkmConfig } from './types.js';\nexport type { LoadedConfig, WorkspaceConfig } from './workspace/index.js';\nexport { defineWorkspace } from './workspace/index.js';\n/**\n * Define GKM configuration with full TypeScript support.\n * This is an identity function that provides type safety and autocomplete.\n *\n * @example\n * ```ts\n * // gkm.config.ts\n * import { defineConfig } from '@geekmidas/cli/config';\n *\n * export default defineConfig({\n * routes: './src/endpoints/**\\/*.ts',\n * envParser: './src/config/env',\n * logger: './src/config/logger',\n * telescope: true,\n * });\n * ```\n */\nexport function defineConfig(config: GkmConfig): GkmConfig {\n\treturn config;\n}\n\nexport interface ParsedModuleConfig {\n\tpath: string;\n\timportPattern: string;\n}\n\n/**\n * Parse a module config string into path and import pattern.\n *\n * @param configString - Config string in format \"./path/to/module\" or \"./path/to/module#exportName\"\n * @param defaultAlias - The default alias name to use if no export name specified\n * @returns Object with path and import pattern\n *\n * @example\n * parseModuleConfig('./src/config/env', 'envParser')\n * // { path: './src/config/env', importPattern: 'envParser' }\n *\n * parseModuleConfig('./src/config/env#envParser', 'envParser')\n * // { path: './src/config/env', importPattern: '{ envParser }' }\n *\n * parseModuleConfig('./src/config/env#myEnv', 'envParser')\n * // { path: './src/config/env', importPattern: '{ myEnv as envParser }' }\n */\nexport function parseModuleConfig(\n\tconfigString: string,\n\tdefaultAlias: string,\n): ParsedModuleConfig {\n\tconst parts = configString.split('#');\n\tconst path = parts[0] ?? configString;\n\tconst exportName = parts[1];\n\tconst importPattern = !exportName\n\t\t? defaultAlias\n\t\t: exportName === defaultAlias\n\t\t\t? `{ ${defaultAlias} }`\n\t\t\t: `{ ${exportName} as ${defaultAlias} }`;\n\n\treturn { path, importPattern };\n}\n\n/**\n * Find and return the path to the config file.\n */\nfunction findConfigPath(cwd: string): string {\n\tconst files = ['gkm.config.json', 'gkm.config.ts', 'gkm.config.js'];\n\n\tfor (const file of files) {\n\t\tconst path = join(cwd, file);\n\t\tif (existsSync(path)) {\n\t\t\treturn path;\n\t\t}\n\t}\n\n\tthrow new Error(\n\t\t'Configuration file not found. Please create gkm.config.json, gkm.config.ts, or gkm.config.js in the project root.',\n\t);\n}\n\n/**\n * Load raw configuration from file.\n */\nasync function loadRawConfig(\n\tcwd: string,\n): Promise<GkmConfig | WorkspaceConfig> {\n\tconst configPath = findConfigPath(cwd);\n\n\ttry {\n\t\tconst config = await import(configPath);\n\t\treturn config.default;\n\t} catch (error) {\n\t\tthrow new Error(`Failed to load config: ${(error as Error).message}`);\n\t}\n}\n\n/**\n * Load configuration file (single-app format).\n * For backwards compatibility with existing code.\n *\n * @deprecated Use loadWorkspaceConfig for new code\n */\nexport async function loadConfig(\n\tcwd: string = process.cwd(),\n): Promise<GkmConfig> {\n\tconst config = await loadRawConfig(cwd);\n\n\t// If it's a workspace config, throw an error\n\tif (isWorkspaceConfig(config)) {\n\t\tthrow new Error(\n\t\t\t'Workspace configuration detected. Use loadWorkspaceConfig() instead.',\n\t\t);\n\t}\n\n\treturn config;\n}\n\n/**\n * Load configuration file and process it as a workspace.\n * Works with both single-app and workspace configurations.\n *\n * Single-app configs are automatically wrapped as a workspace with one app.\n *\n * @example\n * ```ts\n * const { type, workspace } = await loadWorkspaceConfig();\n *\n * if (type === 'workspace') {\n * console.log('Multi-app workspace:', workspace.apps);\n * } else {\n * console.log('Single app wrapped as workspace');\n * }\n * ```\n */\nexport async function loadWorkspaceConfig(\n\tcwd: string = process.cwd(),\n): Promise<LoadedConfig> {\n\tconst config = await loadRawConfig(cwd);\n\treturn processConfig(config, cwd);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,aAAaA,QAA8B;AAC1D,QAAO;AACP;;;;;;;;;;;;;;;;;;AAwBD,SAAgB,kBACfC,cACAC,cACqB;CACrB,MAAM,QAAQ,aAAa,MAAM,IAAI;CACrC,MAAM,OAAO,MAAM,MAAM;CACzB,MAAM,aAAa,MAAM;CACzB,MAAM,iBAAiB,aACpB,eACA,eAAe,gBACb,IAAI,aAAa,OACjB,IAAI,WAAW,MAAM,aAAa;AAEvC,QAAO;EAAE;EAAM;CAAe;AAC9B;;;;AAKD,SAAS,eAAeC,KAAqB;CAC5C,MAAM,QAAQ;EAAC;EAAmB;EAAiB;CAAgB;AAEnE,MAAK,MAAM,QAAQ,OAAO;EACzB,MAAM,OAAO,KAAK,KAAK,KAAK;AAC5B,MAAI,WAAW,KAAK,CACnB,QAAO;CAER;AAED,OAAM,IAAI,MACT;AAED;;;;AAKD,eAAe,cACdA,KACuC;CACvC,MAAM,aAAa,eAAe,IAAI;AAEtC,KAAI;EACH,MAAM,SAAS,MAAM,OAAO;AAC5B,SAAO,OAAO;CACd,SAAQ,OAAO;AACf,QAAM,IAAI,OAAO,yBAA0B,MAAgB,QAAQ;CACnE;AACD;;;;;;;AAQD,eAAsB,WACrBA,MAAc,QAAQ,KAAK,EACN;CACrB,MAAM,SAAS,MAAM,cAAc,IAAI;AAGvC,KAAI,kBAAkB,OAAO,CAC5B,OAAM,IAAI,MACT;AAIF,QAAO;AACP;;;;;;;;;;;;;;;;;;AAmBD,eAAsB,oBACrBA,MAAc,QAAQ,KAAK,EACH;CACxB,MAAM,SAAS,MAAM,cAAc,IAAI;AACvC,QAAO,cAAc,QAAQ,IAAI;AACjC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"config-CxrLu8ia.cjs","names":["config: GkmConfig","configString: string","defaultAlias: string","cwd: string"],"sources":["../src/config.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { GkmConfig } from './types.js';\nimport {\n\tisWorkspaceConfig,\n\ttype LoadedConfig,\n\tprocessConfig,\n\ttype WorkspaceConfig,\n} from './workspace/index.js';\n\nexport type { GkmConfig } from './types.js';\nexport type { LoadedConfig, WorkspaceConfig } from './workspace/index.js';\nexport { defineWorkspace } from './workspace/index.js';\n/**\n * Define GKM configuration with full TypeScript support.\n * This is an identity function that provides type safety and autocomplete.\n *\n * @example\n * ```ts\n * // gkm.config.ts\n * import { defineConfig } from '@geekmidas/cli/config';\n *\n * export default defineConfig({\n * routes: './src/endpoints/**\\/*.ts',\n * envParser: './src/config/env',\n * logger: './src/config/logger',\n * telescope: true,\n * });\n * ```\n */\nexport function defineConfig(config: GkmConfig): GkmConfig {\n\treturn config;\n}\n\nexport interface ParsedModuleConfig {\n\tpath: string;\n\timportPattern: string;\n}\n\n/**\n * Parse a module config string into path and import pattern.\n *\n * @param configString - Config string in format \"./path/to/module\" or \"./path/to/module#exportName\"\n * @param defaultAlias - The default alias name to use if no export name specified\n * @returns Object with path and import pattern\n *\n * @example\n * parseModuleConfig('./src/config/env', 'envParser')\n * // { path: './src/config/env', importPattern: 'envParser' }\n *\n * parseModuleConfig('./src/config/env#envParser', 'envParser')\n * // { path: './src/config/env', importPattern: '{ envParser }' }\n *\n * parseModuleConfig('./src/config/env#myEnv', 'envParser')\n * // { path: './src/config/env', importPattern: '{ myEnv as envParser }' }\n */\nexport function parseModuleConfig(\n\tconfigString: string,\n\tdefaultAlias: string,\n): ParsedModuleConfig {\n\tconst parts = configString.split('#');\n\tconst path = parts[0] ?? configString;\n\tconst exportName = parts[1];\n\tconst importPattern = !exportName\n\t\t? defaultAlias\n\t\t: exportName === defaultAlias\n\t\t\t? `{ ${defaultAlias} }`\n\t\t\t: `{ ${exportName} as ${defaultAlias} }`;\n\n\treturn { path, importPattern };\n}\n\n/**\n * Find and return the path to the config file.\n */\nfunction findConfigPath(cwd: string): string {\n\tconst files = ['gkm.config.json', 'gkm.config.ts', 'gkm.config.js'];\n\n\tfor (const file of files) {\n\t\tconst path = join(cwd, file);\n\t\tif (existsSync(path)) {\n\t\t\treturn path;\n\t\t}\n\t}\n\n\tthrow new Error(\n\t\t'Configuration file not found. Please create gkm.config.json, gkm.config.ts, or gkm.config.js in the project root.',\n\t);\n}\n\n/**\n * Load raw configuration from file.\n */\nasync function loadRawConfig(\n\tcwd: string,\n): Promise<GkmConfig | WorkspaceConfig> {\n\tconst configPath = findConfigPath(cwd);\n\n\ttry {\n\t\tconst config = await import(configPath);\n\t\treturn config.default;\n\t} catch (error) {\n\t\tthrow new Error(`Failed to load config: ${(error as Error).message}`);\n\t}\n}\n\n/**\n * Load configuration file (single-app format).\n * For backwards compatibility with existing code.\n *\n * @deprecated Use loadWorkspaceConfig for new code\n */\nexport async function loadConfig(\n\tcwd: string = process.cwd(),\n): Promise<GkmConfig> {\n\tconst config = await loadRawConfig(cwd);\n\n\t// If it's a workspace config, throw an error\n\tif (isWorkspaceConfig(config)) {\n\t\tthrow new Error(\n\t\t\t'Workspace configuration detected. Use loadWorkspaceConfig() instead.',\n\t\t);\n\t}\n\n\treturn config;\n}\n\n/**\n * Load configuration file and process it as a workspace.\n * Works with both single-app and workspace configurations.\n *\n * Single-app configs are automatically wrapped as a workspace with one app.\n *\n * @example\n * ```ts\n * const { type, workspace } = await loadWorkspaceConfig();\n *\n * if (type === 'workspace') {\n * console.log('Multi-app workspace:', workspace.apps);\n * } else {\n * console.log('Single app wrapped as workspace');\n * }\n * ```\n */\nexport async function loadWorkspaceConfig(\n\tcwd: string = process.cwd(),\n): Promise<LoadedConfig> {\n\tconst config = await loadRawConfig(cwd);\n\treturn processConfig(config, cwd);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,aAAaA,QAA8B;AAC1D,QAAO;AACP;;;;;;;;;;;;;;;;;;AAwBD,SAAgB,kBACfC,cACAC,cACqB;CACrB,MAAM,QAAQ,aAAa,MAAM,IAAI;CACrC,MAAM,OAAO,MAAM,MAAM;CACzB,MAAM,aAAa,MAAM;CACzB,MAAM,iBAAiB,aACpB,eACA,eAAe,gBACb,IAAI,aAAa,OACjB,IAAI,WAAW,MAAM,aAAa;AAEvC,QAAO;EAAE;EAAM;CAAe;AAC9B;;;;AAKD,SAAS,eAAeC,KAAqB;CAC5C,MAAM,QAAQ;EAAC;EAAmB;EAAiB;CAAgB;AAEnE,MAAK,MAAM,QAAQ,OAAO;EACzB,MAAM,OAAO,oBAAK,KAAK,KAAK;AAC5B,MAAI,wBAAW,KAAK,CACnB,QAAO;CAER;AAED,OAAM,IAAI,MACT;AAED;;;;AAKD,eAAe,cACdA,KACuC;CACvC,MAAM,aAAa,eAAe,IAAI;AAEtC,KAAI;EACH,MAAM,SAAS,MAAM,OAAO;AAC5B,SAAO,OAAO;CACd,SAAQ,OAAO;AACf,QAAM,IAAI,OAAO,yBAA0B,MAAgB,QAAQ;CACnE;AACD;;;;;;;AAQD,eAAsB,WACrBA,MAAc,QAAQ,KAAK,EACN;CACrB,MAAM,SAAS,MAAM,cAAc,IAAI;AAGvC,KAAI,oCAAkB,OAAO,CAC5B,OAAM,IAAI,MACT;AAIF,QAAO;AACP;;;;;;;;;;;;;;;;;;AAmBD,eAAsB,oBACrBA,MAAc,QAAQ,KAAK,EACH;CACxB,MAAM,SAAS,MAAM,cAAc,IAAI;AACvC,QAAO,gCAAc,QAAQ,IAAI;AACjC"}