@geekmidas/cli 1.2.2 → 1.3.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 (90) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/{CachedStateProvider-DVyKfaMm.mjs → CachedStateProvider-BDq5WqSy.mjs} +1 -1
  3. package/dist/{CachedStateProvider-DVyKfaMm.mjs.map → CachedStateProvider-BDq5WqSy.mjs.map} +1 -1
  4. package/dist/CachedStateProvider-CI61keQ1.mjs +3 -0
  5. package/dist/{HostingerProvider-DqUq6e9i.mjs → HostingerProvider-B9N-TKbp.mjs} +2 -2
  6. package/dist/{HostingerProvider-DqUq6e9i.mjs.map → HostingerProvider-B9N-TKbp.mjs.map} +1 -1
  7. package/dist/{LocalStateProvider-DxoSaWUV.mjs → LocalStateProvider-BDm7ZqJo.mjs} +1 -1
  8. package/dist/{LocalStateProvider-DxoSaWUV.mjs.map → LocalStateProvider-BDm7ZqJo.mjs.map} +1 -1
  9. package/dist/{Route53Provider-KUAX3vz9.mjs → Route53Provider-DOWmFnwN.mjs} +2 -2
  10. package/dist/{Route53Provider-KUAX3vz9.mjs.map → Route53Provider-DOWmFnwN.mjs.map} +1 -1
  11. package/dist/{Route53Provider-CpRIqu69.cjs → Route53Provider-xrWuBXih.cjs} +2 -2
  12. package/dist/{Route53Provider-CpRIqu69.cjs.map → Route53Provider-xrWuBXih.cjs.map} +1 -1
  13. package/dist/{SSMStateProvider-D79o_JjM.cjs → SSMStateProvider-DGrqYll0.cjs} +8 -4
  14. package/dist/SSMStateProvider-DGrqYll0.cjs.map +1 -0
  15. package/dist/{SSMStateProvider-BjCi_58g.mjs → SSMStateProvider-DT0WV-E_.mjs} +9 -4
  16. package/dist/SSMStateProvider-DT0WV-E_.mjs.map +1 -0
  17. package/dist/{bundler-BqTN5Dj5.mjs → bundler-DgXsOSxc.mjs} +3 -3
  18. package/dist/{bundler-BqTN5Dj5.mjs.map → bundler-DgXsOSxc.mjs.map} +1 -1
  19. package/dist/chunk-Duj1WY3L.mjs +7 -0
  20. package/dist/{config-BQ4a36Rq.mjs → config-C1bidhvG.mjs} +2 -2
  21. package/dist/{config-BQ4a36Rq.mjs.map → config-C1bidhvG.mjs.map} +1 -1
  22. package/dist/{config-Bayob8pB.cjs → config-C1dM7aZb.cjs} +2 -2
  23. package/dist/{config-Bayob8pB.cjs.map → config-C1dM7aZb.cjs.map} +1 -1
  24. package/dist/config.cjs +2 -2
  25. package/dist/config.d.cts +1 -1
  26. package/dist/config.d.mts +2 -2
  27. package/dist/config.mjs +2 -2
  28. package/dist/{credentials-DT1dSxIx.mjs → credentials-s1kLcIzK.mjs} +1 -1
  29. package/dist/{credentials-DT1dSxIx.mjs.map → credentials-s1kLcIzK.mjs.map} +1 -1
  30. package/dist/deploy/sniffer-routes-worker.cjs +65 -0
  31. package/dist/deploy/sniffer-routes-worker.cjs.map +1 -0
  32. package/dist/deploy/sniffer-routes-worker.d.cts +1 -0
  33. package/dist/deploy/sniffer-routes-worker.d.mts +1 -0
  34. package/dist/deploy/sniffer-routes-worker.mjs +64 -0
  35. package/dist/deploy/sniffer-routes-worker.mjs.map +1 -0
  36. package/dist/dokploy-api-DSJYNx88.mjs +3 -0
  37. package/dist/{dokploy-api-7k3t7_zd.mjs → dokploy-api-z0833e7r.mjs} +1 -1
  38. package/dist/{dokploy-api-7k3t7_zd.mjs.map → dokploy-api-z0833e7r.mjs.map} +1 -1
  39. package/dist/{encryption-JtMsiGNp.mjs → encryption-BOH5M-f-.mjs} +1 -1
  40. package/dist/{encryption-JtMsiGNp.mjs.map → encryption-BOH5M-f-.mjs.map} +1 -1
  41. package/dist/encryption-a9TNMWav.mjs +3 -0
  42. package/dist/{index-Bi9vGQJy.d.mts → index-DvpWzLD7.d.mts} +5 -2
  43. package/dist/index-DvpWzLD7.d.mts.map +1 -0
  44. package/dist/{index-CufAAnge.d.cts → index-DzmZ6SUW.d.cts} +4 -1
  45. package/dist/index-DzmZ6SUW.d.cts.map +1 -0
  46. package/dist/index.cjs +100 -170
  47. package/dist/index.cjs.map +1 -1
  48. package/dist/index.mjs +116 -185
  49. package/dist/index.mjs.map +1 -1
  50. package/dist/{openapi-BZ4Qik9w.mjs → openapi-9k6a6VA4.mjs} +3 -3
  51. package/dist/{openapi-BZ4Qik9w.mjs.map → openapi-9k6a6VA4.mjs.map} +1 -1
  52. package/dist/{openapi-CzfnHlhG.cjs → openapi-Dcja4e1C.cjs} +2 -8
  53. package/dist/{openapi-CzfnHlhG.cjs.map → openapi-Dcja4e1C.cjs.map} +1 -1
  54. package/dist/{openapi-react-query-DGEkD39r.mjs → openapi-react-query-DaTMSPD5.mjs} +1 -1
  55. package/dist/{openapi-react-query-DGEkD39r.mjs.map → openapi-react-query-DaTMSPD5.mjs.map} +1 -1
  56. package/dist/openapi-react-query.mjs +1 -1
  57. package/dist/openapi.cjs +3 -3
  58. package/dist/openapi.d.mts +1 -1
  59. package/dist/openapi.mjs +3 -3
  60. package/dist/{storage-BMW6yLu3.mjs → storage-DmCbr6DI.mjs} +1 -1
  61. package/dist/{storage-BMW6yLu3.mjs.map → storage-DmCbr6DI.mjs.map} +1 -1
  62. package/dist/{storage-D8XzjVaO.mjs → storage-Dx_jZbq6.mjs} +1 -1
  63. package/dist/{types-BldpmqQX.d.mts → types-B9UZ7fOG.d.mts} +1 -1
  64. package/dist/{types-BldpmqQX.d.mts.map → types-B9UZ7fOG.d.mts.map} +1 -1
  65. package/dist/workspace/index.cjs +1 -1
  66. package/dist/workspace/index.d.cts +1 -1
  67. package/dist/workspace/index.d.mts +2 -2
  68. package/dist/workspace/index.mjs +1 -1
  69. package/dist/{workspace-CASoZOjs.mjs → workspace-Cb_I7oCJ.mjs} +5 -8
  70. package/dist/{workspace-CASoZOjs.mjs.map → workspace-Cb_I7oCJ.mjs.map} +1 -1
  71. package/dist/{workspace-BMJE18LV.cjs → workspace-CeFgIDC-.cjs} +3 -2
  72. package/dist/{workspace-BMJE18LV.cjs.map → workspace-CeFgIDC-.cjs.map} +1 -1
  73. package/package.json +2 -2
  74. package/src/deploy/SSMStateProvider.ts +14 -3
  75. package/src/deploy/StateProvider.ts +5 -1
  76. package/src/deploy/__tests__/SSMStateProvider.spec.ts +12 -0
  77. package/src/deploy/__tests__/createStateProvider.spec.ts +10 -0
  78. package/src/dev/index.ts +69 -106
  79. package/src/init/generators/web.ts +6 -2
  80. package/src/workspace/__tests__/client-generator.spec.ts +330 -301
  81. package/src/workspace/client-generator.ts +139 -199
  82. package/src/workspace/schema.ts +2 -0
  83. package/tsdown.config.ts +1 -0
  84. package/dist/CachedStateProvider-OiFUGr7p.mjs +0 -3
  85. package/dist/SSMStateProvider-BjCi_58g.mjs.map +0 -1
  86. package/dist/SSMStateProvider-D79o_JjM.cjs.map +0 -1
  87. package/dist/dokploy-api-CHa8G51l.mjs +0 -3
  88. package/dist/encryption-UUmaWAmz.mjs +0 -3
  89. package/dist/index-Bi9vGQJy.d.mts.map +0 -1
  90. package/dist/index-CufAAnge.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-BMJE18LV.cjs');
4
- const require_config = require('./config-Bayob8pB.cjs');
3
+ const require_workspace = require('./workspace-CeFgIDC-.cjs');
4
+ const require_config = require('./config-C1dM7aZb.cjs');
5
5
  const require_credentials = require('./credentials-C8DWtnMY.cjs');
6
- const require_openapi = require('./openapi-CzfnHlhG.cjs');
6
+ const require_openapi = require('./openapi-Dcja4e1C.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.2.1";
35
+ var version = "1.2.3";
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";
@@ -628,99 +628,61 @@ export async function setupSubscribers(
628
628
  //#region src/workspace/client-generator.ts
629
629
  const logger$10 = console;
630
630
  /**
631
- * Cache of OpenAPI spec hashes to detect changes.
632
- */
633
- const specHashCache = /* @__PURE__ */ new Map();
634
- /**
635
- * Calculate hash of content for change detection.
631
+ * Get frontend apps that depend on a backend app.
636
632
  */
637
- function hashContent(content) {
638
- return (0, node_crypto.createHash)("sha256").update(content).digest("hex").slice(0, 16);
633
+ function getDependentFrontends(workspace, backendAppName) {
634
+ const dependentApps = [];
635
+ for (const [appName, app] of Object.entries(workspace.apps)) if (app.type === "frontend" && app.dependencies.includes(backendAppName)) dependentApps.push(appName);
636
+ return dependentApps;
639
637
  }
640
638
  /**
641
- * Normalize routes to an array of patterns.
642
- * @internal Exported for use in dev command
639
+ * Get the path to a backend's OpenAPI spec file.
643
640
  */
644
- function normalizeRoutes(routes) {
645
- if (!routes) return [];
646
- return Array.isArray(routes) ? routes : [routes];
641
+ function getBackendOpenApiPath(workspace, backendAppName) {
642
+ const app = workspace.apps[backendAppName];
643
+ if (!app || app.type !== "backend") return null;
644
+ return (0, node_path.join)(workspace.root, app.path, ".gkm", "openapi.ts");
647
645
  }
648
646
  /**
649
- * Generate OpenAPI spec for a backend app.
650
- * Returns the spec content and endpoint count.
647
+ * Count endpoints in an OpenAPI spec content.
651
648
  */
652
- async function generateBackendOpenApi(workspace, appName) {
653
- const app = workspace.apps[appName];
654
- if (!app || app.type !== "backend" || !app.routes) return null;
655
- const appPath = (0, node_path.join)(workspace.root, app.path);
656
- const routesPatterns = normalizeRoutes(app.routes);
657
- if (routesPatterns.length === 0) return null;
658
- const endpointGenerator = new require_openapi.EndpointGenerator();
659
- const allLoadedEndpoints = [];
660
- for (const pattern of routesPatterns) {
661
- const fullPattern = (0, node_path.join)(appPath, pattern);
662
- const loaded = await endpointGenerator.load(fullPattern);
663
- allLoadedEndpoints.push(...loaded);
664
- }
665
- const loadedEndpoints = allLoadedEndpoints;
666
- if (loadedEndpoints.length === 0) return null;
667
- const endpoints = loadedEndpoints.map(({ construct }) => construct);
668
- const tsGenerator = new require_openapi.OpenApiTsGenerator();
669
- const content = await tsGenerator.generate(endpoints, {
670
- title: `${appName} API`,
671
- version: "1.0.0",
672
- description: `Auto-generated API client for ${appName}`
673
- });
674
- return {
675
- content,
676
- endpointCount: loadedEndpoints.length
677
- };
649
+ function countEndpoints(content) {
650
+ const endpointMatches = content.match(/'(GET|POST|PUT|PATCH|DELETE)\s+\/[^']+'/g);
651
+ return endpointMatches?.length ?? 0;
678
652
  }
679
653
  /**
680
- * Generate client for a frontend app from its backend dependencies.
681
- * Only regenerates if the OpenAPI spec has changed.
654
+ * Copy the OpenAPI client from a backend to all dependent frontend apps.
655
+ * Called when the backend's .gkm/openapi.ts file changes.
682
656
  */
683
- async function generateClientForFrontend(workspace, frontendAppName, options = {}) {
657
+ async function copyClientToFrontends(workspace, backendAppName, options = {}) {
658
+ const log = options.silent ? () => {} : logger$10.log.bind(logger$10);
684
659
  const results = [];
685
- const frontendApp = workspace.apps[frontendAppName];
686
- if (!frontendApp || frontendApp.type !== "frontend") return results;
687
- const dependencies$1 = frontendApp.dependencies || [];
688
- const backendDeps = dependencies$1.filter((dep) => {
689
- const depApp = workspace.apps[dep];
690
- return depApp?.type === "backend" && depApp.routes;
691
- });
692
- if (backendDeps.length === 0) return results;
693
- const clientOutput = frontendApp.client?.output || "src/api";
694
- const frontendPath = (0, node_path.join)(workspace.root, frontendApp.path);
695
- const outputDir = (0, node_path.join)(frontendPath, clientOutput);
696
- for (const backendAppName of backendDeps) {
660
+ const backendApp = workspace.apps[backendAppName];
661
+ if (!backendApp || backendApp.type !== "backend") return results;
662
+ const openApiPath = (0, node_path.join)(workspace.root, backendApp.path, ".gkm", "openapi.ts");
663
+ if (!(0, node_fs.existsSync)(openApiPath)) return results;
664
+ const content = await (0, node_fs_promises.readFile)(openApiPath, "utf-8");
665
+ const endpointCount = countEndpoints(content);
666
+ const dependentFrontends = getDependentFrontends(workspace, backendAppName);
667
+ for (const frontendAppName of dependentFrontends) {
668
+ const frontendApp = workspace.apps[frontendAppName];
669
+ if (!frontendApp || frontendApp.type !== "frontend") continue;
670
+ const clientOutput = frontendApp.client?.output;
671
+ if (!clientOutput) continue;
697
672
  const result = {
698
673
  frontendApp: frontendAppName,
699
674
  backendApp: backendAppName,
700
675
  outputPath: "",
701
- endpointCount: 0,
702
- generated: false
676
+ endpointCount,
677
+ success: false
703
678
  };
704
679
  try {
705
- const spec = await generateBackendOpenApi(workspace, backendAppName);
706
- if (!spec) {
707
- result.reason = "No endpoints found in backend";
708
- results.push(result);
709
- continue;
710
- }
711
- result.endpointCount = spec.endpointCount;
712
- const cacheKey = `${backendAppName}:${frontendAppName}`;
713
- const newHash = hashContent(spec.content);
714
- const oldHash = specHashCache.get(cacheKey);
715
- if (!options.force && oldHash === newHash) {
716
- result.reason = "No schema changes detected";
717
- results.push(result);
718
- continue;
719
- }
680
+ const frontendPath = (0, node_path.join)(workspace.root, frontendApp.path);
681
+ const outputDir = (0, node_path.join)(frontendPath, clientOutput);
720
682
  await (0, node_fs_promises.mkdir)(outputDir, { recursive: true });
721
- const fileName = backendDeps.length === 1 ? "openapi.ts" : `${backendAppName}-api.ts`;
683
+ const fileName = `${backendAppName}.ts`;
722
684
  const outputPath = (0, node_path.join)(outputDir, fileName);
723
- const backendRelPath = (0, node_path.relative)((0, node_path.dirname)(outputPath), (0, node_path.join)(workspace.root, workspace.apps[backendAppName].path));
685
+ const backendRelPath = (0, node_path.relative)((0, node_path.dirname)(outputPath), (0, node_path.join)(workspace.root, backendApp.path));
724
686
  const clientContent = `/**
725
687
  * Auto-generated API client for ${backendAppName}
726
688
  * Generated from: ${backendRelPath}
@@ -728,43 +690,31 @@ async function generateClientForFrontend(workspace, frontendAppName, options = {
728
690
  * DO NOT EDIT - This file is automatically regenerated when backend schemas change.
729
691
  */
730
692
 
731
- ${spec.content}
693
+ ${content}
732
694
  `;
733
695
  await (0, node_fs_promises.writeFile)(outputPath, clientContent);
734
- specHashCache.set(cacheKey, newHash);
735
696
  result.outputPath = outputPath;
736
- result.generated = true;
737
- results.push(result);
697
+ result.success = true;
698
+ log(`📦 Copied client to ${frontendAppName} from ${backendAppName} (${endpointCount} endpoints)`);
738
699
  } catch (error) {
739
- result.reason = `Error: ${error.message}`;
740
- results.push(result);
700
+ result.error = error.message;
741
701
  }
702
+ results.push(result);
742
703
  }
743
704
  return results;
744
705
  }
745
706
  /**
746
- * Generate clients for all frontend apps in the workspace.
707
+ * Copy clients from all backends to their dependent frontends.
708
+ * Useful for initial setup or force refresh.
747
709
  */
748
- async function generateAllClients(workspace, options = {}) {
749
- const log = options.silent ? () => {} : logger$10.log.bind(logger$10);
710
+ async function copyAllClients(workspace, options = {}) {
750
711
  const allResults = [];
751
- for (const [appName, app] of Object.entries(workspace.apps)) if (app.type === "frontend" && app.dependencies.length > 0) {
752
- const results = await generateClientForFrontend(workspace, appName, { force: options.force });
753
- for (const result of results) {
754
- if (result.generated) log(`📦 Generated client for ${result.frontendApp} from ${result.backendApp} (${result.endpointCount} endpoints)`);
755
- allResults.push(result);
756
- }
712
+ for (const [appName, app] of Object.entries(workspace.apps)) if (app.type === "backend" && app.routes) {
713
+ const results = await copyClientToFrontends(workspace, appName, options);
714
+ allResults.push(...results);
757
715
  }
758
716
  return allResults;
759
717
  }
760
- /**
761
- * Get frontend apps that depend on a backend app.
762
- */
763
- function getDependentFrontends(workspace, backendAppName) {
764
- const dependentApps = [];
765
- for (const [appName, app] of Object.entries(workspace.apps)) if (app.type === "frontend" && app.dependencies.includes(backendAppName)) dependentApps.push(appName);
766
- return dependentApps;
767
- }
768
718
 
769
719
  //#endregion
770
720
  //#region src/dev/index.ts
@@ -1264,10 +1214,10 @@ async function workspaceDevCommand(workspace, options) {
1264
1214
  if (hasErrors) throw new Error("Frontend app validation failed. Fix the issues above and try again.");
1265
1215
  logger$9.log("✅ Frontend apps validated");
1266
1216
  }
1267
- if (frontendApps.length > 0) {
1268
- const clientResults = await generateAllClients(workspace, { force: true });
1269
- const generatedCount = clientResults.filter((r) => r.generated).length;
1270
- if (generatedCount > 0) logger$9.log(`\n📦 Generated ${generatedCount} API client(s)`);
1217
+ if (frontendApps.length > 0 && backendApps.length > 0) {
1218
+ const clientResults = await copyAllClients(workspace);
1219
+ const copiedCount = clientResults.filter((r) => r.success).length;
1220
+ if (copiedCount > 0) logger$9.log(`\n📦 Copied ${copiedCount} API client(s)`);
1271
1221
  }
1272
1222
  await startWorkspaceServices(workspace);
1273
1223
  const secretsEnv = await loadDevSecrets(workspace);
@@ -1328,65 +1278,42 @@ async function workspaceDevCommand(workspace, options) {
1328
1278
  stdio: "inherit",
1329
1279
  env: turboEnv
1330
1280
  });
1331
- let endpointWatcher = null;
1281
+ let openApiWatcher = null;
1332
1282
  if (frontendApps.length > 0 && backendApps.length > 0) {
1333
- const watchPatterns = [];
1334
- const backendRouteMap = /* @__PURE__ */ new Map();
1335
- for (const [appName, app] of backendApps) {
1336
- const routePatterns = normalizeRoutes(app.routes);
1337
- for (const routePattern of routePatterns) {
1338
- const fullPattern = (0, node_path.join)(workspace.root, app.path, routePattern);
1339
- watchPatterns.push(fullPattern);
1340
- const patternKey = (0, node_path.join)(app.path, routePattern);
1341
- const existing = backendRouteMap.get(patternKey) || [];
1342
- backendRouteMap.set(patternKey, [...existing, appName]);
1343
- }
1283
+ const openApiPaths = [];
1284
+ for (const [appName] of backendApps) {
1285
+ const openApiPath = getBackendOpenApiPath(workspace, appName);
1286
+ if (openApiPath) openApiPaths.push({
1287
+ path: openApiPath,
1288
+ appName
1289
+ });
1344
1290
  }
1345
- if (watchPatterns.length > 0) {
1346
- const resolvedFiles = await (0, fast_glob.default)(watchPatterns, {
1347
- cwd: workspace.root,
1348
- absolute: true,
1349
- onlyFiles: true
1291
+ if (openApiPaths.length > 0) {
1292
+ logger$9.log(`\n👀 Watching ${openApiPaths.length} backend OpenAPI spec(s) for changes`);
1293
+ const pathToApp = new Map(openApiPaths.map((p) => [p.path, p.appName]));
1294
+ openApiWatcher = chokidar.default.watch(openApiPaths.map((p) => p.path), {
1295
+ persistent: true,
1296
+ ignoreInitial: true,
1297
+ depth: 0
1350
1298
  });
1351
- if (resolvedFiles.length > 0) {
1352
- logger$9.log(`\n👀 Watching ${resolvedFiles.length} endpoint file(s) for schema changes`);
1353
- endpointWatcher = chokidar.default.watch(resolvedFiles, {
1354
- ignored: /(^|[/\\])\../,
1355
- persistent: true,
1356
- ignoreInitial: true
1357
- });
1358
- let regenerateTimeout = null;
1359
- endpointWatcher.on("change", async (changedPath) => {
1360
- if (regenerateTimeout) clearTimeout(regenerateTimeout);
1361
- regenerateTimeout = setTimeout(async () => {
1362
- const changedBackends = [];
1363
- for (const [appName, app] of backendApps) {
1364
- const routePatterns = normalizeRoutes(app.routes);
1365
- for (const routePattern of routePatterns) {
1366
- const routesDir = (0, node_path.join)(workspace.root, app.path, routePattern.split("*")[0] || "");
1367
- if (changedPath.startsWith(routesDir.replace(/\/$/, ""))) {
1368
- changedBackends.push(appName);
1369
- break;
1370
- }
1371
- }
1372
- }
1373
- if (changedBackends.length === 0) return;
1374
- const affectedFrontends = /* @__PURE__ */ new Set();
1375
- for (const backend of changedBackends) {
1376
- const dependents = getDependentFrontends(workspace, backend);
1377
- for (const frontend of dependents) affectedFrontends.add(frontend);
1378
- }
1379
- if (affectedFrontends.size === 0) return;
1380
- logger$9.log(`\n🔄 Detected schema change in ${changedBackends.join(", ")}`);
1381
- for (const frontend of affectedFrontends) try {
1382
- const results = await generateClientForFrontend(workspace, frontend);
1383
- for (const result of results) if (result.generated) logger$9.log(` 📦 Regenerated client for ${result.frontendApp} (${result.endpointCount} endpoints)`);
1384
- } catch (error) {
1385
- logger$9.error(` ❌ Failed to regenerate client for ${frontend}: ${error.message}`);
1386
- }
1387
- }, 500);
1388
- });
1389
- }
1299
+ let copyTimeout = null;
1300
+ const handleChange = async (changedPath) => {
1301
+ if (copyTimeout) clearTimeout(copyTimeout);
1302
+ copyTimeout = setTimeout(async () => {
1303
+ const backendAppName = pathToApp.get(changedPath);
1304
+ if (!backendAppName) return;
1305
+ logger$9.log(`\n🔄 OpenAPI spec changed for ${backendAppName}`);
1306
+ try {
1307
+ const results = await copyClientToFrontends(workspace, backendAppName, { silent: true });
1308
+ for (const result of results) if (result.success) logger$9.log(` 📦 Copied client to ${result.frontendApp} (${result.endpointCount} endpoints)`);
1309
+ else if (result.error) logger$9.error(` ❌ Failed to copy client to ${result.frontendApp}: ${result.error}`);
1310
+ } catch (error) {
1311
+ logger$9.error(` ❌ Failed to copy clients: ${error.message}`);
1312
+ }
1313
+ }, 200);
1314
+ };
1315
+ openApiWatcher.on("change", handleChange);
1316
+ openApiWatcher.on("add", handleChange);
1390
1317
  }
1391
1318
  }
1392
1319
  let isShuttingDown = false;
@@ -1394,7 +1321,7 @@ async function workspaceDevCommand(workspace, options) {
1394
1321
  if (isShuttingDown) return;
1395
1322
  isShuttingDown = true;
1396
1323
  logger$9.log("\n🛑 Shutting down workspace...");
1397
- if (endpointWatcher) endpointWatcher.close().catch(() => {});
1324
+ if (openApiWatcher) openApiWatcher.close().catch(() => {});
1398
1325
  if (turboProcess.pid) try {
1399
1326
  process.kill(-turboProcess.pid, "SIGTERM");
1400
1327
  } catch {
@@ -1412,7 +1339,7 @@ async function workspaceDevCommand(workspace, options) {
1412
1339
  reject(error);
1413
1340
  });
1414
1341
  turboProcess.on("exit", (code) => {
1415
- if (endpointWatcher) endpointWatcher.close().catch(() => {});
1342
+ if (openApiWatcher) openApiWatcher.close().catch(() => {});
1416
1343
  if (code !== null && code !== 0) reject(new Error(`Turbo exited with code ${code}`));
1417
1344
  else resolve$3();
1418
1345
  });
@@ -2276,7 +2203,7 @@ async function createDnsProvider(options) {
2276
2203
  return new HostingerProvider();
2277
2204
  }
2278
2205
  if (provider === "route53") {
2279
- const { Route53Provider } = await Promise.resolve().then(() => require("./Route53Provider-CpRIqu69.cjs"));
2206
+ const { Route53Provider } = await Promise.resolve().then(() => require("./Route53Provider-xrWuBXih.cjs"));
2280
2207
  const route53Config = config;
2281
2208
  return new Route53Provider({
2282
2209
  region: route53Config.region,
@@ -4543,12 +4470,14 @@ async function createStateProvider(options) {
4543
4470
  if (provider === "ssm") {
4544
4471
  if (!workspaceName) throw new Error("Workspace name is required for SSM state provider. Set \"name\" in gkm.config.ts.");
4545
4472
  const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-CdspeSVL.cjs"));
4546
- const { SSMStateProvider } = await Promise.resolve().then(() => require("./SSMStateProvider-D79o_JjM.cjs"));
4473
+ const { SSMStateProvider } = await Promise.resolve().then(() => require("./SSMStateProvider-DGrqYll0.cjs"));
4547
4474
  const { CachedStateProvider: CachedStateProvider$1 } = await Promise.resolve().then(() => require("./CachedStateProvider-D_uISMmJ.cjs"));
4475
+ const ssmConfig = config;
4548
4476
  const local = new LocalStateProvider(workspaceRoot);
4549
4477
  const ssm = SSMStateProvider.create({
4550
4478
  workspaceName,
4551
- region: config.region
4479
+ region: ssmConfig.region,
4480
+ profile: ssmConfig.profile
4552
4481
  });
4553
4482
  return new CachedStateProvider$1(ssm, local);
4554
4483
  }
@@ -9978,14 +9907,14 @@ export default nextConfig;
9978
9907
  jsx: "preserve",
9979
9908
  incremental: true,
9980
9909
  plugins: [{ name: "next" }],
9910
+ baseUrl: ".",
9981
9911
  paths: {
9982
- "~/*": ["./src/*"],
9912
+ "~/*": ["./src/*", "../../packages/ui/src/*"],
9983
9913
  [`${modelsPackage}`]: ["../../packages/models/src"],
9984
9914
  [`${modelsPackage}/*`]: ["../../packages/models/src/*"],
9985
9915
  [`${uiPackage}`]: ["../../packages/ui/src"],
9986
9916
  [`${uiPackage}/*`]: ["../../packages/ui/src/*"]
9987
- },
9988
- baseUrl: "."
9917
+ }
9989
9918
  },
9990
9919
  include: [
9991
9920
  "next-env.d.ts",
@@ -9993,7 +9922,8 @@ export default nextConfig;
9993
9922
  "**/*.tsx",
9994
9923
  ".next/types/**/*.ts"
9995
9924
  ],
9996
- exclude: ["node_modules"]
9925
+ exclude: ["node_modules"],
9926
+ references: [{ path: "../../packages/ui" }, { path: "../../packages/models" }]
9997
9927
  };
9998
9928
  const queryClientTs = `import { QueryClient } from '@tanstack/react-query';
9999
9929