@geekmidas/cli 1.2.1 → 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/CHANGELOG.md +12 -0
- package/dist/index.cjs +110 -182
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +111 -183
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-DrbBWq0s.mjs → openapi-NthphEWK.mjs} +3 -4
- package/dist/{openapi-DrbBWq0s.mjs.map → openapi-NthphEWK.mjs.map} +1 -1
- package/dist/{openapi-BZP8jkI4.cjs → openapi-ZhO7wwya.cjs} +2 -9
- package/dist/{openapi-BZP8jkI4.cjs.map → openapi-ZhO7wwya.cjs.map} +1 -1
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.d.cts.map +1 -1
- package/dist/openapi.d.mts.map +1 -1
- package/dist/openapi.mjs +1 -1
- package/package.json +4 -4
- package/src/__tests__/openapi.spec.ts +385 -5
- package/src/dev/index.ts +71 -107
- package/src/generators/OpenApiTsGenerator.ts +0 -1
- package/src/init/generators/ui.ts +20 -20
- package/src/openapi.ts +2 -1
- package/src/workspace/__tests__/client-generator.spec.ts +472 -19
- package/src/workspace/client-generator.ts +139 -199
package/src/dev/index.ts
CHANGED
|
@@ -46,10 +46,9 @@ import type {
|
|
|
46
46
|
TelescopeConfig,
|
|
47
47
|
} from '../types';
|
|
48
48
|
import {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
//
|
|
968
|
-
if (frontendApps.length > 0) {
|
|
969
|
-
const clientResults = await
|
|
970
|
-
const
|
|
971
|
-
if (
|
|
972
|
-
logger.log(`\n📦
|
|
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
|
|
1062
|
-
let
|
|
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
|
|
1066
|
-
const
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
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 (
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
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
|
-
|
|
1091
|
-
|
|
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
|
-
|
|
1096
|
-
|
|
1082
|
+
openApiWatcher = chokidar.watch(
|
|
1083
|
+
openApiPaths.map((p) => p.path),
|
|
1084
|
+
{
|
|
1097
1085
|
persistent: true,
|
|
1098
1086
|
ignoreInitial: true,
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1087
|
+
// Watch parent directory too since file may not exist yet
|
|
1088
|
+
depth: 0,
|
|
1089
|
+
},
|
|
1090
|
+
);
|
|
1102
1091
|
|
|
1103
|
-
|
|
1104
|
-
// Debounce regeneration
|
|
1105
|
-
if (regenerateTimeout) {
|
|
1106
|
-
clearTimeout(regenerateTimeout);
|
|
1107
|
-
}
|
|
1092
|
+
let copyTimeout: NodeJS.Timeout | null = null;
|
|
1108
1093
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
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
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
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
|
-
|
|
1142
|
-
return;
|
|
1143
|
-
}
|
|
1106
|
+
logger.log(`\n🔄 OpenAPI spec changed for ${backendAppName}`);
|
|
1144
1107
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1108
|
+
try {
|
|
1109
|
+
const results = await copyClientToFrontends(
|
|
1110
|
+
workspace,
|
|
1111
|
+
backendAppName,
|
|
1112
|
+
{ silent: true },
|
|
1148
1113
|
);
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
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
|
-
|
|
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
|
|
1121
|
+
` ❌ Failed to copy client to ${result.frontendApp}: ${result.error}`,
|
|
1166
1122
|
);
|
|
1167
1123
|
}
|
|
1168
1124
|
}
|
|
1169
|
-
}
|
|
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
|
|
1184
|
-
if (
|
|
1185
|
-
|
|
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 (
|
|
1218
|
-
|
|
1180
|
+
if (openApiWatcher) {
|
|
1181
|
+
openApiWatcher.close().catch(() => {});
|
|
1219
1182
|
}
|
|
1220
1183
|
|
|
1221
1184
|
if (code !== null && code !== 0) {
|
|
@@ -1294,11 +1257,12 @@ function generateCredentialsInjection(secretsJsonPath: string): string {
|
|
|
1294
1257
|
return `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
1295
1258
|
import { existsSync, readFileSync } from 'node:fs';
|
|
1296
1259
|
|
|
1297
|
-
// Inject dev secrets into Credentials
|
|
1260
|
+
// Inject dev secrets into Credentials and process.env
|
|
1298
1261
|
const secretsPath = '${secretsJsonPath}';
|
|
1299
1262
|
if (existsSync(secretsPath)) {
|
|
1300
1263
|
const secrets = JSON.parse(readFileSync(secretsPath, 'utf-8'));
|
|
1301
1264
|
Object.assign(Credentials, secrets);
|
|
1265
|
+
Object.assign(process.env, secrets);
|
|
1302
1266
|
// Debug: uncomment to verify preload is running
|
|
1303
1267
|
// console.log('[gkm preload] Injected', Object.keys(secrets).length, 'credentials');
|
|
1304
1268
|
}
|
|
@@ -722,7 +722,6 @@ export function createApi(options: CreateApiOptions) {
|
|
|
722
722
|
`;
|
|
723
723
|
|
|
724
724
|
return `// Auto-generated by @geekmidas/cli - DO NOT EDIT
|
|
725
|
-
// Generated: ${new Date().toISOString()}
|
|
726
725
|
|
|
727
726
|
// ============================================================
|
|
728
727
|
// Security Scheme Type
|
|
@@ -317,7 +317,7 @@ export { Button, buttonVariants };
|
|
|
317
317
|
|
|
318
318
|
// src/components/ui/button/button.stories.tsx
|
|
319
319
|
const buttonStories = `import type { Meta, StoryObj } from '@storybook/react';
|
|
320
|
-
import { Button } from '
|
|
320
|
+
import { Button } from '~/components/ui/button';
|
|
321
321
|
|
|
322
322
|
const meta: Meta<typeof Button> = {
|
|
323
323
|
title: 'Components/Button',
|
|
@@ -488,7 +488,7 @@ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
|
|
488
488
|
|
|
489
489
|
// src/components/ui/input/input.stories.tsx
|
|
490
490
|
const inputStories = `import type { Meta, StoryObj } from '@storybook/react';
|
|
491
|
-
import { Input } from '
|
|
491
|
+
import { Input } from '~/components/ui/input';
|
|
492
492
|
|
|
493
493
|
const meta: Meta<typeof Input> = {
|
|
494
494
|
title: 'Components/Input',
|
|
@@ -544,7 +544,7 @@ export const WithValue: Story = {
|
|
|
544
544
|
|
|
545
545
|
// src/components/ui/card/card.stories.tsx
|
|
546
546
|
const cardStories = `import type { Meta, StoryObj } from '@storybook/react';
|
|
547
|
-
import { Button } from '
|
|
547
|
+
import { Button } from '~/components/ui/button';
|
|
548
548
|
import {
|
|
549
549
|
Card,
|
|
550
550
|
CardContent,
|
|
@@ -552,8 +552,8 @@ import {
|
|
|
552
552
|
CardFooter,
|
|
553
553
|
CardHeader,
|
|
554
554
|
CardTitle,
|
|
555
|
-
} from '
|
|
556
|
-
import { Input } from '
|
|
555
|
+
} from '~/components/ui/card';
|
|
556
|
+
import { Input } from '~/components/ui/input';
|
|
557
557
|
|
|
558
558
|
const meta: Meta<typeof Card> = {
|
|
559
559
|
title: 'Components/Card',
|
|
@@ -635,8 +635,8 @@ export { Label };
|
|
|
635
635
|
|
|
636
636
|
// src/components/ui/label/label.stories.tsx
|
|
637
637
|
const labelStories = `import type { Meta, StoryObj } from '@storybook/react';
|
|
638
|
-
import { Input } from '
|
|
639
|
-
import { Label } from '
|
|
638
|
+
import { Input } from '~/components/ui/input';
|
|
639
|
+
import { Label } from '~/components/ui/label';
|
|
640
640
|
|
|
641
641
|
const meta: Meta<typeof Label> = {
|
|
642
642
|
title: 'Components/Label',
|
|
@@ -715,7 +715,7 @@ export { Badge, badgeVariants };
|
|
|
715
715
|
|
|
716
716
|
// src/components/ui/badge/badge.stories.tsx
|
|
717
717
|
const badgeStories = `import type { Meta, StoryObj } from '@storybook/react';
|
|
718
|
-
import { Badge } from '
|
|
718
|
+
import { Badge } from '~/components/ui/badge';
|
|
719
719
|
|
|
720
720
|
const meta: Meta<typeof Badge> = {
|
|
721
721
|
title: 'Components/Badge',
|
|
@@ -795,7 +795,7 @@ export { Separator };
|
|
|
795
795
|
|
|
796
796
|
// src/components/ui/separator/separator.stories.tsx
|
|
797
797
|
const separatorStories = `import type { Meta, StoryObj } from '@storybook/react';
|
|
798
|
-
import { Separator } from '
|
|
798
|
+
import { Separator } from '~/components/ui/separator';
|
|
799
799
|
|
|
800
800
|
const meta: Meta<typeof Separator> = {
|
|
801
801
|
title: 'Components/Separator',
|
|
@@ -904,11 +904,11 @@ export { Tabs, TabsList, TabsTrigger, TabsContent };
|
|
|
904
904
|
|
|
905
905
|
// src/components/ui/tabs/tabs.stories.tsx
|
|
906
906
|
const tabsStories = `import type { Meta, StoryObj } from '@storybook/react';
|
|
907
|
-
import { Tabs, TabsContent, TabsList, TabsTrigger } from '
|
|
908
|
-
import { Button } from '
|
|
909
|
-
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '
|
|
910
|
-
import { Input } from '
|
|
911
|
-
import { Label } from '
|
|
907
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from '~/components/ui/tabs';
|
|
908
|
+
import { Button } from '~/components/ui/button';
|
|
909
|
+
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '~/components/ui/card';
|
|
910
|
+
import { Input } from '~/components/ui/input';
|
|
911
|
+
import { Label } from '~/components/ui/label';
|
|
912
912
|
|
|
913
913
|
const meta: Meta<typeof Tabs> = {
|
|
914
914
|
title: 'Components/Tabs',
|
|
@@ -1012,8 +1012,8 @@ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
|
|
1012
1012
|
|
|
1013
1013
|
// src/components/ui/tooltip/tooltip.stories.tsx
|
|
1014
1014
|
const tooltipStories = `import type { Meta, StoryObj } from '@storybook/react';
|
|
1015
|
-
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '
|
|
1016
|
-
import { Button } from '
|
|
1015
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '~/components/ui/tooltip';
|
|
1016
|
+
import { Button } from '~/components/ui/button';
|
|
1017
1017
|
|
|
1018
1018
|
const meta: Meta<typeof Tooltip> = {
|
|
1019
1019
|
title: 'Components/Tooltip',
|
|
@@ -1217,10 +1217,10 @@ import {
|
|
|
1217
1217
|
DialogHeader,
|
|
1218
1218
|
DialogTitle,
|
|
1219
1219
|
DialogTrigger,
|
|
1220
|
-
} from '
|
|
1221
|
-
import { Button } from '
|
|
1222
|
-
import { Input } from '
|
|
1223
|
-
import { Label } from '
|
|
1220
|
+
} from '~/components/ui/dialog';
|
|
1221
|
+
import { Button } from '~/components/ui/button';
|
|
1222
|
+
import { Input } from '~/components/ui/input';
|
|
1223
|
+
import { Label } from '~/components/ui/label';
|
|
1224
1224
|
|
|
1225
1225
|
const meta: Meta<typeof Dialog> = {
|
|
1226
1226
|
title: 'Components/Dialog',
|
package/src/openapi.ts
CHANGED
|
@@ -27,8 +27,9 @@ export function resolveOpenApiConfig(
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
if (config.openapi === true || config.openapi === undefined) {
|
|
30
|
+
// Enable by default when not explicitly set (undefined) or explicitly true
|
|
30
31
|
return {
|
|
31
|
-
enabled:
|
|
32
|
+
enabled: true,
|
|
32
33
|
title: 'API Documentation',
|
|
33
34
|
version: '1.0.0',
|
|
34
35
|
description: 'Auto-generated API documentation from endpoints',
|