@geekmidas/cli 1.2.2 → 1.2.3

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/openapi.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  require('./workspace-BMJE18LV.cjs');
3
3
  require('./config-Bayob8pB.cjs');
4
- const require_openapi = require('./openapi-CzfnHlhG.cjs');
4
+ const require_openapi = require('./openapi-ZhO7wwya.cjs');
5
5
 
6
6
  exports.OPENAPI_OUTPUT_PATH = require_openapi.OPENAPI_OUTPUT_PATH;
7
7
  exports.generateOpenApi = require_openapi.generateOpenApi;
package/dist/openapi.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  import "./workspace-CASoZOjs.mjs";
3
3
  import "./config-BQ4a36Rq.mjs";
4
- import { OPENAPI_OUTPUT_PATH, generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi-BZ4Qik9w.mjs";
4
+ import { OPENAPI_OUTPUT_PATH, generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi-NthphEWK.mjs";
5
5
 
6
6
  export { OPENAPI_OUTPUT_PATH, generateOpenApi, openapiCommand, resolveOpenApiConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/cli",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "description": "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs",
5
5
  "private": false,
6
6
  "type": "module",
@@ -54,8 +54,8 @@
54
54
  "prompts": "~2.4.2",
55
55
  "tsx": "~4.20.3",
56
56
  "@geekmidas/constructs": "~1.0.0",
57
- "@geekmidas/errors": "~1.0.0",
58
57
  "@geekmidas/envkit": "~1.0.0",
58
+ "@geekmidas/errors": "~1.0.0",
59
59
  "@geekmidas/logger": "~1.0.0",
60
60
  "@geekmidas/schema": "~1.0.0"
61
61
  },
package/src/dev/index.ts CHANGED
@@ -46,10 +46,9 @@ import type {
46
46
  TelescopeConfig,
47
47
  } from '../types';
48
48
  import {
49
- generateAllClients,
50
- generateClientForFrontend,
51
- getDependentFrontends,
52
- normalizeRoutes,
49
+ copyAllClients,
50
+ copyClientToFrontends,
51
+ getBackendOpenApiPath,
53
52
  } from '../workspace/client-generator.js';
54
53
  import {
55
54
  getAppBuildOrder,
@@ -964,12 +963,12 @@ async function workspaceDevCommand(
964
963
  logger.log('✅ Frontend apps validated');
965
964
  }
966
965
 
967
- // Generate initial clients for frontends with backend dependencies
968
- if (frontendApps.length > 0) {
969
- const clientResults = await generateAllClients(workspace, { force: true });
970
- const generatedCount = clientResults.filter((r) => r.generated).length;
971
- if (generatedCount > 0) {
972
- logger.log(`\n📦 Generated ${generatedCount} API client(s)`);
966
+ // Copy initial clients from backends to frontends
967
+ if (frontendApps.length > 0 && backendApps.length > 0) {
968
+ const clientResults = await copyAllClients(workspace);
969
+ const copiedCount = clientResults.filter((r) => r.success).length;
970
+ if (copiedCount > 0) {
971
+ logger.log(`\n📦 Copied ${copiedCount} API client(s)`);
973
972
  }
974
973
  }
975
974
 
@@ -1058,117 +1057,81 @@ async function workspaceDevCommand(
1058
1057
  env: turboEnv,
1059
1058
  });
1060
1059
 
1061
- // Set up file watcher for backend endpoint changes (smart client regeneration)
1062
- let endpointWatcher: ReturnType<typeof chokidar.watch> | null = null;
1060
+ // Set up file watcher for backend .gkm/openapi.ts changes (auto-copy to frontends)
1061
+ let openApiWatcher: ReturnType<typeof chokidar.watch> | null = null;
1063
1062
 
1064
1063
  if (frontendApps.length > 0 && backendApps.length > 0) {
1065
- // Collect all backend route patterns to watch
1066
- const watchPatterns: string[] = [];
1067
- const backendRouteMap = new Map<string, string[]>(); // routes pattern -> backend app names
1068
-
1069
- for (const [appName, app] of backendApps) {
1070
- const routePatterns = normalizeRoutes(app.routes);
1071
- for (const routePattern of routePatterns) {
1072
- const fullPattern = join(workspace.root, app.path, routePattern);
1073
- watchPatterns.push(fullPattern);
1074
-
1075
- // Map pattern to app name for change detection
1076
- const patternKey = join(app.path, routePattern);
1077
- const existing = backendRouteMap.get(patternKey) || [];
1078
- backendRouteMap.set(patternKey, [...existing, appName]);
1064
+ // Collect all backend openapi.ts file paths to watch
1065
+ const openApiPaths: { path: string; appName: string }[] = [];
1066
+
1067
+ for (const [appName] of backendApps) {
1068
+ const openApiPath = getBackendOpenApiPath(workspace, appName);
1069
+ if (openApiPath) {
1070
+ openApiPaths.push({ path: openApiPath, appName });
1079
1071
  }
1080
1072
  }
1081
1073
 
1082
- if (watchPatterns.length > 0) {
1083
- // Resolve glob patterns to files
1084
- const resolvedFiles = await fg(watchPatterns, {
1085
- cwd: workspace.root,
1086
- absolute: true,
1087
- onlyFiles: true,
1088
- });
1074
+ if (openApiPaths.length > 0) {
1075
+ logger.log(
1076
+ `\n👀 Watching ${openApiPaths.length} backend OpenAPI spec(s) for changes`,
1077
+ );
1089
1078
 
1090
- if (resolvedFiles.length > 0) {
1091
- logger.log(
1092
- `\n👀 Watching ${resolvedFiles.length} endpoint file(s) for schema changes`,
1093
- );
1079
+ // Create a map for quick lookup of app name from path
1080
+ const pathToApp = new Map(openApiPaths.map((p) => [p.path, p.appName]));
1094
1081
 
1095
- endpointWatcher = chokidar.watch(resolvedFiles, {
1096
- ignored: /(^|[/\\])\../,
1082
+ openApiWatcher = chokidar.watch(
1083
+ openApiPaths.map((p) => p.path),
1084
+ {
1097
1085
  persistent: true,
1098
1086
  ignoreInitial: true,
1099
- });
1100
-
1101
- let regenerateTimeout: NodeJS.Timeout | null = null;
1087
+ // Watch parent directory too since file may not exist yet
1088
+ depth: 0,
1089
+ },
1090
+ );
1102
1091
 
1103
- endpointWatcher.on('change', async (changedPath) => {
1104
- // Debounce regeneration
1105
- if (regenerateTimeout) {
1106
- clearTimeout(regenerateTimeout);
1107
- }
1092
+ let copyTimeout: NodeJS.Timeout | null = null;
1108
1093
 
1109
- regenerateTimeout = setTimeout(async () => {
1110
- // Find which backend app this file belongs to
1111
- const changedBackends: string[] = [];
1112
-
1113
- for (const [appName, app] of backendApps) {
1114
- const routePatterns = normalizeRoutes(app.routes);
1115
- for (const routePattern of routePatterns) {
1116
- const routesDir = join(
1117
- workspace.root,
1118
- app.path,
1119
- routePattern.split('*')[0] || '',
1120
- );
1121
- if (changedPath.startsWith(routesDir.replace(/\/$/, ''))) {
1122
- changedBackends.push(appName);
1123
- break; // Found a match, no need to check other patterns
1124
- }
1125
- }
1126
- }
1127
-
1128
- if (changedBackends.length === 0) {
1129
- return;
1130
- }
1094
+ const handleChange = async (changedPath: string) => {
1095
+ // Debounce to handle rapid changes
1096
+ if (copyTimeout) {
1097
+ clearTimeout(copyTimeout);
1098
+ }
1131
1099
 
1132
- // Find frontends that depend on changed backends
1133
- const affectedFrontends = new Set<string>();
1134
- for (const backend of changedBackends) {
1135
- const dependents = getDependentFrontends(workspace, backend);
1136
- for (const frontend of dependents) {
1137
- affectedFrontends.add(frontend);
1138
- }
1139
- }
1100
+ copyTimeout = setTimeout(async () => {
1101
+ const backendAppName = pathToApp.get(changedPath);
1102
+ if (!backendAppName) {
1103
+ return;
1104
+ }
1140
1105
 
1141
- if (affectedFrontends.size === 0) {
1142
- return;
1143
- }
1106
+ logger.log(`\n🔄 OpenAPI spec changed for ${backendAppName}`);
1144
1107
 
1145
- // Regenerate clients for affected frontends
1146
- logger.log(
1147
- `\n🔄 Detected schema change in ${changedBackends.join(', ')}`,
1108
+ try {
1109
+ const results = await copyClientToFrontends(
1110
+ workspace,
1111
+ backendAppName,
1112
+ { silent: true },
1148
1113
  );
1149
-
1150
- for (const frontend of affectedFrontends) {
1151
- try {
1152
- const results = await generateClientForFrontend(
1153
- workspace,
1154
- frontend,
1114
+ for (const result of results) {
1115
+ if (result.success) {
1116
+ logger.log(
1117
+ ` 📦 Copied client to ${result.frontendApp} (${result.endpointCount} endpoints)`,
1155
1118
  );
1156
- for (const result of results) {
1157
- if (result.generated) {
1158
- logger.log(
1159
- ` 📦 Regenerated client for ${result.frontendApp} (${result.endpointCount} endpoints)`,
1160
- );
1161
- }
1162
- }
1163
- } catch (error) {
1119
+ } else if (result.error) {
1164
1120
  logger.error(
1165
- ` ❌ Failed to regenerate client for ${frontend}: ${(error as Error).message}`,
1121
+ ` ❌ Failed to copy client to ${result.frontendApp}: ${result.error}`,
1166
1122
  );
1167
1123
  }
1168
1124
  }
1169
- }, 500); // 500ms debounce
1170
- });
1171
- }
1125
+ } catch (error) {
1126
+ logger.error(
1127
+ ` ❌ Failed to copy clients: ${(error as Error).message}`,
1128
+ );
1129
+ }
1130
+ }, 200); // 200ms debounce
1131
+ };
1132
+
1133
+ openApiWatcher.on('change', handleChange);
1134
+ openApiWatcher.on('add', handleChange);
1172
1135
  }
1173
1136
  }
1174
1137
 
@@ -1180,9 +1143,9 @@ async function workspaceDevCommand(
1180
1143
 
1181
1144
  logger.log('\n🛑 Shutting down workspace...');
1182
1145
 
1183
- // Close endpoint watcher
1184
- if (endpointWatcher) {
1185
- endpointWatcher.close().catch(() => {});
1146
+ // Close OpenAPI watcher
1147
+ if (openApiWatcher) {
1148
+ openApiWatcher.close().catch(() => {});
1186
1149
  }
1187
1150
 
1188
1151
  // Kill turbo process
@@ -1214,8 +1177,8 @@ async function workspaceDevCommand(
1214
1177
 
1215
1178
  turboProcess.on('exit', (code) => {
1216
1179
  // Close watcher on exit
1217
- if (endpointWatcher) {
1218
- endpointWatcher.close().catch(() => {});
1180
+ if (openApiWatcher) {
1181
+ openApiWatcher.close().catch(() => {});
1219
1182
  }
1220
1183
 
1221
1184
  if (code !== null && code !== 0) {