@geekmidas/cli 0.0.25 → 0.1.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/FUNCTION_CRON_SUPPORT.md +266 -0
- package/README.md +21 -4
- package/dist/CronGenerator-ClbRcmz_.mjs +53 -0
- package/dist/CronGenerator-ClbRcmz_.mjs.map +1 -0
- package/dist/CronGenerator-Ctl4USy4.cjs +59 -0
- package/dist/CronGenerator-Ctl4USy4.cjs.map +1 -0
- package/dist/EndpointGenerator-Dj7AumHi.cjs +164 -0
- package/dist/EndpointGenerator-Dj7AumHi.cjs.map +1 -0
- package/dist/EndpointGenerator-uBA1ixUw.mjs +158 -0
- package/dist/EndpointGenerator-uBA1ixUw.mjs.map +1 -0
- package/dist/FunctionGenerator-DN681IUn.cjs +58 -0
- package/dist/FunctionGenerator-DN681IUn.cjs.map +1 -0
- package/dist/FunctionGenerator-crAa-JC7.mjs +52 -0
- package/dist/FunctionGenerator-crAa-JC7.mjs.map +1 -0
- package/dist/Generator-C3tYSTQY.cjs +47 -0
- package/dist/Generator-C3tYSTQY.cjs.map +1 -0
- package/dist/Generator-CDt4pB3W.mjs +41 -0
- package/dist/Generator-CDt4pB3W.mjs.map +1 -0
- package/dist/__tests__/config.spec.cjs +98 -0
- package/dist/__tests__/config.spec.cjs.map +1 -0
- package/dist/__tests__/config.spec.mjs +97 -0
- package/dist/__tests__/config.spec.mjs.map +1 -0
- package/dist/__tests__/test-helpers.cjs +14 -0
- package/dist/__tests__/test-helpers.mjs +4 -0
- package/dist/build/__tests__/index-new.spec.cjs +286 -0
- package/dist/build/__tests__/index-new.spec.cjs.map +1 -0
- package/dist/build/__tests__/index-new.spec.mjs +285 -0
- package/dist/build/__tests__/index-new.spec.mjs.map +1 -0
- package/dist/build/index.cjs +11 -0
- package/dist/build/index.mjs +11 -0
- package/dist/build/manifests.cjs +3 -0
- package/dist/build/manifests.mjs +3 -0
- package/dist/build/providerResolver.cjs +5 -0
- package/dist/build/providerResolver.mjs +3 -0
- package/dist/build/types.cjs +0 -0
- package/dist/build/types.mjs +0 -0
- package/dist/build-BZdwxCLW.mjs +64 -0
- package/dist/build-BZdwxCLW.mjs.map +1 -0
- package/dist/build-BfQFnU5-.cjs +70 -0
- package/dist/build-BfQFnU5-.cjs.map +1 -0
- package/dist/{chunk-CUT6urMc.cjs → chunk-CsX-DzYB.cjs} +12 -0
- package/dist/config-CXxYmz_o.mjs +30 -0
- package/dist/config-CXxYmz_o.mjs.map +1 -0
- package/dist/config-RcNESK0T.cjs +36 -0
- package/dist/config-RcNESK0T.cjs.map +1 -0
- package/dist/config.cjs +1 -1
- package/dist/config.mjs +1 -1
- package/dist/esm-9eeZntth.mjs +3777 -0
- package/dist/esm-9eeZntth.mjs.map +1 -0
- package/dist/esm-Crmo4h9t.cjs +4392 -0
- package/dist/esm-Crmo4h9t.cjs.map +1 -0
- package/dist/esm-CsJbr7gi.mjs +3 -0
- package/dist/esm-w09tAC4l.cjs +8 -0
- package/dist/generators/CronGenerator.cjs +4 -0
- package/dist/generators/CronGenerator.mjs +4 -0
- package/dist/generators/EndpointGenerator.cjs +4 -0
- package/dist/generators/EndpointGenerator.mjs +4 -0
- package/dist/generators/FunctionGenerator.cjs +4 -0
- package/dist/generators/FunctionGenerator.mjs +4 -0
- package/dist/generators/Generator.cjs +3 -0
- package/dist/generators/Generator.mjs +3 -0
- package/dist/generators/__tests__/CronGenerator.spec.cjs +216 -0
- package/dist/generators/__tests__/CronGenerator.spec.cjs.map +1 -0
- package/dist/generators/__tests__/CronGenerator.spec.mjs +215 -0
- package/dist/generators/__tests__/CronGenerator.spec.mjs.map +1 -0
- package/dist/generators/__tests__/EndpointGenerator.spec.cjs +182 -0
- package/dist/generators/__tests__/EndpointGenerator.spec.cjs.map +1 -0
- package/dist/generators/__tests__/EndpointGenerator.spec.mjs +181 -0
- package/dist/generators/__tests__/EndpointGenerator.spec.mjs.map +1 -0
- package/dist/generators/__tests__/FunctionGenerator.spec.cjs +152 -0
- package/dist/generators/__tests__/FunctionGenerator.spec.cjs.map +1 -0
- package/dist/generators/__tests__/FunctionGenerator.spec.mjs +151 -0
- package/dist/generators/__tests__/FunctionGenerator.spec.mjs.map +1 -0
- package/dist/generators/index.cjs +10 -0
- package/dist/generators/index.mjs +7 -0
- package/dist/generators-CsLujGXs.mjs +0 -0
- package/dist/generators-_pY7sHy1.cjs +0 -0
- package/dist/index.cjs +68 -26
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +67 -25
- package/dist/index.mjs.map +1 -0
- package/dist/manifests-BTtfDMX8.cjs +26 -0
- package/dist/manifests-BTtfDMX8.cjs.map +1 -0
- package/dist/manifests-HX4z4kkz.mjs +20 -0
- package/dist/manifests-HX4z4kkz.mjs.map +1 -0
- package/dist/{openapi-CksVdkh2.mjs → openapi-BivnatiC.mjs} +8 -6
- package/dist/openapi-BivnatiC.mjs.map +1 -0
- package/dist/{openapi-D4QQJUPY.cjs → openapi-DW-qF3oW.cjs} +9 -7
- package/dist/openapi-DW-qF3oW.cjs.map +1 -0
- package/dist/{openapi-react-query-C1JLYUOs.cjs → openapi-react-query-J0BzBHhN.cjs} +4 -3
- package/dist/openapi-react-query-J0BzBHhN.cjs.map +1 -0
- package/dist/{openapi-react-query-DpT3XHFC.mjs → openapi-react-query-lgS7AVEz.mjs} +3 -2
- package/dist/openapi-react-query-lgS7AVEz.mjs.map +1 -0
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.cjs +4 -3
- package/dist/openapi.mjs +4 -3
- package/dist/providerResolver-B_TjNF0_.mjs +96 -0
- package/dist/providerResolver-B_TjNF0_.mjs.map +1 -0
- package/dist/providerResolver-Cs-0YCaP.cjs +114 -0
- package/dist/providerResolver-Cs-0YCaP.cjs.map +1 -0
- package/dist/test-helpers-ARd8GDgx.cjs +199 -0
- package/dist/test-helpers-ARd8GDgx.cjs.map +1 -0
- package/dist/test-helpers-DdVBk23F.mjs +133 -0
- package/dist/test-helpers-DdVBk23F.mjs.map +1 -0
- package/examples/cron-example.ts +45 -0
- package/examples/function-example.ts +40 -0
- package/examples/gkm.config.json +22 -0
- package/examples/gkm.minimal.config.json +7 -0
- package/examples/gkm.production.config.json +27 -0
- package/package.json +35 -14
- package/src/__tests__/config.spec.ts +110 -0
- package/src/__tests__/test-helpers.ts +178 -0
- package/src/build/__tests__/index-new.spec.ts +578 -0
- package/src/build/index.ts +136 -0
- package/src/build/manifests.ts +32 -0
- package/src/build/providerResolver.ts +184 -0
- package/src/build/types.ts +37 -0
- package/src/config.ts +14 -6
- package/src/generators/CronGenerator.ts +97 -0
- package/src/generators/EndpointGenerator.ts +290 -0
- package/src/generators/FunctionGenerator.ts +96 -0
- package/src/generators/Generator.ts +95 -0
- package/src/generators/__tests__/CronGenerator.spec.ts +445 -0
- package/src/generators/__tests__/EndpointGenerator.spec.ts +372 -0
- package/src/generators/__tests__/FunctionGenerator.spec.ts +257 -0
- package/src/generators/index.ts +8 -0
- package/src/index.ts +57 -22
- package/src/openapi.ts +4 -3
- package/src/types.ts +73 -2
- package/dist/build-BTggTCYL.cjs +0 -176
- package/dist/build-Ca4P6_lY.mjs +0 -170
- package/dist/build.cjs +0 -5
- package/dist/build.mjs +0 -5
- package/dist/config-BNqUMsvc.cjs +0 -24
- package/dist/config-BciAdY6_.mjs +0 -18
- package/dist/loadEndpoints-BBIavB9h.cjs +0 -37
- package/dist/loadEndpoints-DAZ53Og2.mjs +0 -31
- package/dist/loadEndpoints.cjs +0 -3
- package/dist/loadEndpoints.mjs +0 -3
- package/src/build.ts +0 -305
- package/src/loadEndpoints.ts +0 -48
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join, relative } from 'path';
|
|
3
|
+
import type {
|
|
4
|
+
BuildManifest,
|
|
5
|
+
CronInfo,
|
|
6
|
+
FunctionInfo,
|
|
7
|
+
RouteInfo,
|
|
8
|
+
} from '../types';
|
|
9
|
+
|
|
10
|
+
const logger = console;
|
|
11
|
+
|
|
12
|
+
export async function generateManifests(
|
|
13
|
+
outputDir: string,
|
|
14
|
+
routes: RouteInfo[],
|
|
15
|
+
functions: FunctionInfo[],
|
|
16
|
+
crons: CronInfo[],
|
|
17
|
+
): Promise<void> {
|
|
18
|
+
// Generate unified manifest for all providers
|
|
19
|
+
const manifest: BuildManifest = {
|
|
20
|
+
routes,
|
|
21
|
+
functions,
|
|
22
|
+
crons,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const manifestPath = join(outputDir, 'manifest.json');
|
|
26
|
+
await writeFile(manifestPath, JSON.stringify(manifest, null, 2));
|
|
27
|
+
|
|
28
|
+
logger.log(
|
|
29
|
+
`Generated ${routes.length} routes, ${functions.length} functions, ${crons.length} crons in ${relative(process.cwd(), outputDir)}`,
|
|
30
|
+
);
|
|
31
|
+
logger.log(`Manifest: ${relative(process.cwd(), manifestPath)}`);
|
|
32
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AWSApiGatewayConfig,
|
|
3
|
+
AWSLambdaConfig,
|
|
4
|
+
BuildOptions,
|
|
5
|
+
GkmConfig,
|
|
6
|
+
LegacyProvider,
|
|
7
|
+
MainProvider,
|
|
8
|
+
ProvidersConfig,
|
|
9
|
+
ServerConfig,
|
|
10
|
+
} from '../types';
|
|
11
|
+
|
|
12
|
+
export interface ResolvedProviders {
|
|
13
|
+
providers: LegacyProvider[];
|
|
14
|
+
enableOpenApi: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Resolves provider configuration from the new simplified system
|
|
19
|
+
* to the internal legacy format for backward compatibility
|
|
20
|
+
*/
|
|
21
|
+
export function resolveProviders(
|
|
22
|
+
config: GkmConfig,
|
|
23
|
+
options: BuildOptions,
|
|
24
|
+
): ResolvedProviders {
|
|
25
|
+
const providers: LegacyProvider[] = [];
|
|
26
|
+
let enableOpenApi = options.enableOpenApi || false;
|
|
27
|
+
|
|
28
|
+
// Handle legacy providers option (deprecated)
|
|
29
|
+
if (options.providers) {
|
|
30
|
+
return {
|
|
31
|
+
providers: options.providers,
|
|
32
|
+
enableOpenApi,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Handle new provider option
|
|
37
|
+
if (options.provider) {
|
|
38
|
+
const resolvedProviders = resolveMainProvider(
|
|
39
|
+
options.provider,
|
|
40
|
+
config.providers,
|
|
41
|
+
);
|
|
42
|
+
providers.push(...resolvedProviders.providers);
|
|
43
|
+
enableOpenApi = resolvedProviders.enableOpenApi || enableOpenApi;
|
|
44
|
+
}
|
|
45
|
+
// Default: build all configured providers
|
|
46
|
+
else if (config.providers) {
|
|
47
|
+
const resolvedProviders = resolveAllConfiguredProviders(config.providers);
|
|
48
|
+
providers.push(...resolvedProviders.providers);
|
|
49
|
+
enableOpenApi = resolvedProviders.enableOpenApi || enableOpenApi;
|
|
50
|
+
}
|
|
51
|
+
// Fallback: use default AWS configuration
|
|
52
|
+
else {
|
|
53
|
+
providers.push('aws-apigatewayv2', 'aws-lambda');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
providers: [...new Set(providers)], // Remove duplicates
|
|
58
|
+
enableOpenApi,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function resolveMainProvider(
|
|
63
|
+
mainProvider: MainProvider,
|
|
64
|
+
providersConfig?: ProvidersConfig,
|
|
65
|
+
): ResolvedProviders {
|
|
66
|
+
const providers: LegacyProvider[] = [];
|
|
67
|
+
let enableOpenApi = false;
|
|
68
|
+
|
|
69
|
+
if (mainProvider === 'aws') {
|
|
70
|
+
const awsConfig = providersConfig?.aws;
|
|
71
|
+
|
|
72
|
+
// Resolve API Gateway providers
|
|
73
|
+
if (awsConfig?.apiGateway) {
|
|
74
|
+
if (isEnabled(awsConfig.apiGateway.v1)) {
|
|
75
|
+
providers.push('aws-apigatewayv1');
|
|
76
|
+
}
|
|
77
|
+
if (isEnabled(awsConfig.apiGateway.v2)) {
|
|
78
|
+
providers.push('aws-apigatewayv2');
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
// Default: enable v2 if no specific config
|
|
82
|
+
providers.push('aws-apigatewayv2');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Resolve Lambda providers
|
|
86
|
+
if (awsConfig?.lambda) {
|
|
87
|
+
if (
|
|
88
|
+
isEnabled(awsConfig.lambda.functions) ||
|
|
89
|
+
isEnabled(awsConfig.lambda.crons)
|
|
90
|
+
) {
|
|
91
|
+
providers.push('aws-lambda');
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
// Default: enable lambda if no specific config
|
|
95
|
+
providers.push('aws-lambda');
|
|
96
|
+
}
|
|
97
|
+
} else if (mainProvider === 'server') {
|
|
98
|
+
providers.push('server');
|
|
99
|
+
const serverConfig = providersConfig?.server;
|
|
100
|
+
|
|
101
|
+
if (typeof serverConfig === 'object' && serverConfig?.enableOpenApi) {
|
|
102
|
+
enableOpenApi = true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return { providers, enableOpenApi };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function resolveAllConfiguredProviders(
|
|
110
|
+
providersConfig: ProvidersConfig,
|
|
111
|
+
): ResolvedProviders {
|
|
112
|
+
const providers: LegacyProvider[] = [];
|
|
113
|
+
let enableOpenApi = false;
|
|
114
|
+
|
|
115
|
+
// AWS providers
|
|
116
|
+
if (providersConfig.aws) {
|
|
117
|
+
const awsProviders = resolveMainProvider('aws', providersConfig);
|
|
118
|
+
providers.push(...awsProviders.providers);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Server provider
|
|
122
|
+
if (providersConfig.server && isEnabled(providersConfig.server)) {
|
|
123
|
+
providers.push('server');
|
|
124
|
+
if (
|
|
125
|
+
typeof providersConfig.server === 'object' &&
|
|
126
|
+
providersConfig.server.enableOpenApi
|
|
127
|
+
) {
|
|
128
|
+
enableOpenApi = true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return { providers, enableOpenApi };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function isEnabled(
|
|
136
|
+
config:
|
|
137
|
+
| boolean
|
|
138
|
+
| AWSApiGatewayConfig
|
|
139
|
+
| AWSLambdaConfig
|
|
140
|
+
| ServerConfig
|
|
141
|
+
| undefined,
|
|
142
|
+
): boolean {
|
|
143
|
+
if (config === undefined) return false;
|
|
144
|
+
if (typeof config === 'boolean') return config;
|
|
145
|
+
return config.enabled !== false; // Default to true if enabled is not explicitly false
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Gets configuration for a specific AWS service
|
|
150
|
+
*/
|
|
151
|
+
export function getAWSServiceConfig<
|
|
152
|
+
T extends AWSApiGatewayConfig | AWSLambdaConfig,
|
|
153
|
+
>(
|
|
154
|
+
config: GkmConfig,
|
|
155
|
+
service: 'apiGateway' | 'lambda',
|
|
156
|
+
subService?: 'v1' | 'v2' | 'functions' | 'crons',
|
|
157
|
+
): T | undefined {
|
|
158
|
+
const awsConfig = config.providers?.aws;
|
|
159
|
+
if (!awsConfig) return undefined;
|
|
160
|
+
|
|
161
|
+
if (service === 'apiGateway' && awsConfig.apiGateway) {
|
|
162
|
+
const apiConfig = subService
|
|
163
|
+
? awsConfig.apiGateway[subService as 'v1' | 'v2']
|
|
164
|
+
: undefined;
|
|
165
|
+
return typeof apiConfig === 'object' ? (apiConfig as T) : undefined;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (service === 'lambda' && awsConfig.lambda) {
|
|
169
|
+
const lambdaConfig = subService
|
|
170
|
+
? awsConfig.lambda[subService as 'functions' | 'crons']
|
|
171
|
+
: undefined;
|
|
172
|
+
return typeof lambdaConfig === 'object' ? (lambdaConfig as T) : undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Gets server configuration
|
|
180
|
+
*/
|
|
181
|
+
export function getServerConfig(config: GkmConfig): ServerConfig | undefined {
|
|
182
|
+
const serverConfig = config.providers?.server;
|
|
183
|
+
return typeof serverConfig === 'object' ? serverConfig : undefined;
|
|
184
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Cron, Function } from '@geekmidas/api/constructs';
|
|
2
|
+
import type { Endpoint } from '@geekmidas/api/server';
|
|
3
|
+
|
|
4
|
+
import type { CronInfo, FunctionInfo, RouteInfo } from '../types';
|
|
5
|
+
|
|
6
|
+
export interface ProcessedEndpoint {
|
|
7
|
+
file: string;
|
|
8
|
+
exportName: string;
|
|
9
|
+
endpoint: Endpoint<any, any, any, any, any, any>;
|
|
10
|
+
routeInfo: RouteInfo;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ProcessedFunction {
|
|
14
|
+
file: string;
|
|
15
|
+
exportName: string;
|
|
16
|
+
fn: Function<any, any, any, any, any>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ProcessedCron {
|
|
20
|
+
file: string;
|
|
21
|
+
exportName: string;
|
|
22
|
+
cron: Cron<any, any, any, any>;
|
|
23
|
+
schedule?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface BuildContext {
|
|
27
|
+
envParserPath: string;
|
|
28
|
+
envParserImportPattern: string;
|
|
29
|
+
loggerPath: string;
|
|
30
|
+
loggerImportPattern: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ProviderBuildResult {
|
|
34
|
+
routes: RouteInfo[];
|
|
35
|
+
functions: FunctionInfo[];
|
|
36
|
+
crons: CronInfo[];
|
|
37
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
import { existsSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
|
-
import { readFile } from 'fs/promises';
|
|
4
3
|
import type { GkmConfig } from './types.ts';
|
|
5
4
|
|
|
6
5
|
export async function loadConfig(): Promise<GkmConfig> {
|
|
7
|
-
const
|
|
6
|
+
const files = ['gkm.config.json', 'gkm.config.ts', 'gkm.config.js'];
|
|
7
|
+
let configPath = '';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
for (const file of files) {
|
|
10
|
+
const path = join(process.cwd(), file);
|
|
11
|
+
if (existsSync(path)) {
|
|
12
|
+
configPath = path;
|
|
13
|
+
break;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (!configPath) {
|
|
10
18
|
throw new Error(
|
|
11
|
-
'
|
|
19
|
+
'Configuration file not found. Please create gkm.config.json, gkm.config.ts, or gkm.config.js in the project root.',
|
|
12
20
|
);
|
|
13
21
|
}
|
|
14
22
|
|
|
15
23
|
try {
|
|
16
|
-
const config = await
|
|
17
|
-
return
|
|
24
|
+
const config = await import(configPath);
|
|
25
|
+
return config.default;
|
|
18
26
|
} catch (error) {
|
|
19
27
|
throw new Error(
|
|
20
28
|
`Failed to load gkm.config.json: ${(error as Error).message}`,
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join, relative } from 'node:path';
|
|
3
|
+
import { Cron } from '@geekmidas/api/constructs';
|
|
4
|
+
import type { BuildContext } from '../build/types';
|
|
5
|
+
import type { CronInfo } from '../types';
|
|
6
|
+
import {
|
|
7
|
+
ConstructGenerator,
|
|
8
|
+
type GeneratedConstruct,
|
|
9
|
+
type GeneratorOptions,
|
|
10
|
+
} from './Generator';
|
|
11
|
+
|
|
12
|
+
export class CronGenerator extends ConstructGenerator<
|
|
13
|
+
Cron<any, any, any, any>,
|
|
14
|
+
CronInfo[]
|
|
15
|
+
> {
|
|
16
|
+
async build(
|
|
17
|
+
context: BuildContext,
|
|
18
|
+
constructs: GeneratedConstruct<Cron<any, any, any, any>>[],
|
|
19
|
+
outputDir: string,
|
|
20
|
+
options?: GeneratorOptions,
|
|
21
|
+
): Promise<CronInfo[]> {
|
|
22
|
+
const provider = options?.provider || 'aws-lambda';
|
|
23
|
+
const logger = console;
|
|
24
|
+
const cronInfos: CronInfo[] = [];
|
|
25
|
+
|
|
26
|
+
if (constructs.length === 0 || provider !== 'aws-lambda') {
|
|
27
|
+
return cronInfos;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Create crons subdirectory
|
|
31
|
+
const cronsDir = join(outputDir, 'crons');
|
|
32
|
+
await mkdir(cronsDir, { recursive: true });
|
|
33
|
+
|
|
34
|
+
// Generate cron handlers
|
|
35
|
+
for (const { key, construct, path } of constructs) {
|
|
36
|
+
const handlerFile = await this.generateCronHandler(
|
|
37
|
+
cronsDir,
|
|
38
|
+
path.relative,
|
|
39
|
+
key,
|
|
40
|
+
context,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
cronInfos.push({
|
|
44
|
+
name: key,
|
|
45
|
+
handler: relative(process.cwd(), handlerFile).replace(
|
|
46
|
+
/\.ts$/,
|
|
47
|
+
'.handler',
|
|
48
|
+
),
|
|
49
|
+
schedule: construct.schedule || 'rate(1 hour)',
|
|
50
|
+
timeout: construct.timeout,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
logger.log(`Generated cron handler: ${key}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return cronInfos;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
isConstruct(value: any): value is Cron<any, any, any, any> {
|
|
60
|
+
return Cron.isCron(value);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private async generateCronHandler(
|
|
64
|
+
outputDir: string,
|
|
65
|
+
sourceFile: string,
|
|
66
|
+
exportName: string,
|
|
67
|
+
context: BuildContext,
|
|
68
|
+
): Promise<string> {
|
|
69
|
+
const handlerFileName = `${exportName}.ts`;
|
|
70
|
+
const handlerPath = join(outputDir, handlerFileName);
|
|
71
|
+
|
|
72
|
+
const relativePath = relative(dirname(handlerPath), sourceFile);
|
|
73
|
+
const importPath = relativePath.replace(/\.ts$/, '.js');
|
|
74
|
+
|
|
75
|
+
const relativeEnvParserPath = relative(
|
|
76
|
+
dirname(handlerPath),
|
|
77
|
+
context.envParserPath,
|
|
78
|
+
);
|
|
79
|
+
const relativeLoggerPath = relative(
|
|
80
|
+
dirname(handlerPath),
|
|
81
|
+
context.loggerPath,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const content = `import { AWSScheduledFunction } from '@geekmidas/api/aws-lambda';
|
|
85
|
+
import { ${exportName} } from '${importPath}';
|
|
86
|
+
import ${context.envParserImportPattern} from '${relativeEnvParserPath}';
|
|
87
|
+
import ${context.loggerImportPattern} from '${relativeLoggerPath}';
|
|
88
|
+
|
|
89
|
+
const adapter = new AWSScheduledFunction(envParser, ${exportName});
|
|
90
|
+
|
|
91
|
+
export const handler = adapter.handler;
|
|
92
|
+
`;
|
|
93
|
+
|
|
94
|
+
await writeFile(handlerPath, content);
|
|
95
|
+
return handlerPath;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join, relative } from 'node:path';
|
|
3
|
+
import { Endpoint } from '@geekmidas/api/server';
|
|
4
|
+
import type { BuildContext } from '../build/types';
|
|
5
|
+
import type { LegacyProvider, RouteInfo } from '../types';
|
|
6
|
+
import {
|
|
7
|
+
ConstructGenerator,
|
|
8
|
+
type GeneratedConstruct,
|
|
9
|
+
type GeneratorOptions,
|
|
10
|
+
} from './Generator';
|
|
11
|
+
|
|
12
|
+
export class EndpointGenerator extends ConstructGenerator<
|
|
13
|
+
Endpoint<any, any, any, any, any, any>,
|
|
14
|
+
RouteInfo[]
|
|
15
|
+
> {
|
|
16
|
+
isConstruct(value: any): value is Endpoint<any, any, any, any, any, any> {
|
|
17
|
+
return Endpoint.isEndpoint(value);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async build(
|
|
21
|
+
context: BuildContext,
|
|
22
|
+
constructs: GeneratedConstruct<Endpoint<any, any, any, any, any, any>>[],
|
|
23
|
+
outputDir: string,
|
|
24
|
+
options?: GeneratorOptions,
|
|
25
|
+
): Promise<RouteInfo[]> {
|
|
26
|
+
const provider = options?.provider || 'aws-apigatewayv2';
|
|
27
|
+
const enableOpenApi = options?.enableOpenApi || false;
|
|
28
|
+
const logger = console;
|
|
29
|
+
const routes: RouteInfo[] = [];
|
|
30
|
+
|
|
31
|
+
if (constructs.length === 0) {
|
|
32
|
+
return routes;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (provider === 'server') {
|
|
36
|
+
// Generate single server file with all endpoints
|
|
37
|
+
const serverFile = await this.generateServerFile(
|
|
38
|
+
outputDir,
|
|
39
|
+
constructs,
|
|
40
|
+
context,
|
|
41
|
+
enableOpenApi,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
routes.push({
|
|
45
|
+
path: '*',
|
|
46
|
+
method: 'ALL',
|
|
47
|
+
handler: relative(process.cwd(), serverFile),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
logger.log(
|
|
51
|
+
`Generated server app with ${constructs.length} endpoints${enableOpenApi ? ' (OpenAPI enabled)' : ''}`,
|
|
52
|
+
);
|
|
53
|
+
} else if (provider === 'aws-lambda') {
|
|
54
|
+
// For aws-lambda, create routes subdirectory
|
|
55
|
+
const routesDir = join(outputDir, 'routes');
|
|
56
|
+
await mkdir(routesDir, { recursive: true });
|
|
57
|
+
|
|
58
|
+
// Generate individual handlers for API Gateway routes
|
|
59
|
+
for (const { key, construct, path } of constructs) {
|
|
60
|
+
const handlerFile = await this.generateHandlerFile(
|
|
61
|
+
routesDir,
|
|
62
|
+
path.relative,
|
|
63
|
+
key,
|
|
64
|
+
'aws-apigatewayv2',
|
|
65
|
+
construct,
|
|
66
|
+
context,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const routeInfo: RouteInfo = {
|
|
70
|
+
path: construct._path,
|
|
71
|
+
method: construct.method,
|
|
72
|
+
handler: relative(process.cwd(), handlerFile).replace(
|
|
73
|
+
/\.ts$/,
|
|
74
|
+
'.handler',
|
|
75
|
+
),
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
routes.push(routeInfo);
|
|
79
|
+
logger.log(
|
|
80
|
+
`Generated handler for ${routeInfo.method} ${routeInfo.path}`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
// Generate individual handler files for AWS API Gateway providers
|
|
85
|
+
for (const { key, construct, path } of constructs) {
|
|
86
|
+
const handlerFile = await this.generateHandlerFile(
|
|
87
|
+
outputDir,
|
|
88
|
+
path.relative,
|
|
89
|
+
key,
|
|
90
|
+
provider,
|
|
91
|
+
construct,
|
|
92
|
+
context,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const routeInfo: RouteInfo = {
|
|
96
|
+
path: construct._path,
|
|
97
|
+
method: construct.method,
|
|
98
|
+
handler: relative(process.cwd(), handlerFile).replace(
|
|
99
|
+
/\.ts$/,
|
|
100
|
+
'.handler',
|
|
101
|
+
),
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
routes.push(routeInfo);
|
|
105
|
+
logger.log(
|
|
106
|
+
`Generated handler for ${routeInfo.method} ${routeInfo.path}`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return routes;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private async generateHandlerFile(
|
|
115
|
+
outputDir: string,
|
|
116
|
+
sourceFile: string,
|
|
117
|
+
exportName: string,
|
|
118
|
+
provider: LegacyProvider,
|
|
119
|
+
_endpoint: Endpoint<any, any, any, any, any, any>,
|
|
120
|
+
context: BuildContext,
|
|
121
|
+
): Promise<string> {
|
|
122
|
+
const handlerFileName = `${exportName}.ts`;
|
|
123
|
+
const handlerPath = join(outputDir, handlerFileName);
|
|
124
|
+
|
|
125
|
+
const relativePath = relative(dirname(handlerPath), sourceFile);
|
|
126
|
+
const importPath = relativePath.replace(/\.ts$/, '.js');
|
|
127
|
+
|
|
128
|
+
const relativeEnvParserPath = relative(
|
|
129
|
+
dirname(handlerPath),
|
|
130
|
+
context.envParserPath,
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
let content: string;
|
|
134
|
+
|
|
135
|
+
switch (provider) {
|
|
136
|
+
case 'aws-apigatewayv1':
|
|
137
|
+
content = this.generateAWSApiGatewayV1Handler(
|
|
138
|
+
importPath,
|
|
139
|
+
exportName,
|
|
140
|
+
relativeEnvParserPath,
|
|
141
|
+
context.envParserImportPattern,
|
|
142
|
+
);
|
|
143
|
+
break;
|
|
144
|
+
case 'aws-apigatewayv2':
|
|
145
|
+
content = this.generateAWSApiGatewayV2Handler(
|
|
146
|
+
importPath,
|
|
147
|
+
exportName,
|
|
148
|
+
relativeEnvParserPath,
|
|
149
|
+
context.envParserImportPattern,
|
|
150
|
+
);
|
|
151
|
+
break;
|
|
152
|
+
case 'server':
|
|
153
|
+
content = this.generateServerHandler(importPath, exportName);
|
|
154
|
+
break;
|
|
155
|
+
default:
|
|
156
|
+
throw new Error(`Unsupported provider: ${provider}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
await writeFile(handlerPath, content);
|
|
160
|
+
return handlerPath;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private async generateServerFile(
|
|
164
|
+
outputDir: string,
|
|
165
|
+
endpoints: GeneratedConstruct<Endpoint<any, any, any, any, any, any>>[],
|
|
166
|
+
context: BuildContext,
|
|
167
|
+
enableOpenApi: boolean,
|
|
168
|
+
): Promise<string> {
|
|
169
|
+
const serverFileName = 'app.ts';
|
|
170
|
+
const serverPath = join(outputDir, serverFileName);
|
|
171
|
+
|
|
172
|
+
// Group imports by file
|
|
173
|
+
const importsByFile = new Map<string, string[]>();
|
|
174
|
+
|
|
175
|
+
for (const { path, key } of endpoints) {
|
|
176
|
+
const relativePath = relative(dirname(serverPath), path.relative);
|
|
177
|
+
const importPath = relativePath.replace(/\.ts$/, '.js');
|
|
178
|
+
|
|
179
|
+
if (!importsByFile.has(importPath)) {
|
|
180
|
+
importsByFile.set(importPath, []);
|
|
181
|
+
}
|
|
182
|
+
importsByFile.get(importPath)!.push(key);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const relativeEnvParserPath = relative(
|
|
186
|
+
dirname(serverPath),
|
|
187
|
+
context.envParserPath,
|
|
188
|
+
);
|
|
189
|
+
const relativeLoggerPath = relative(
|
|
190
|
+
dirname(serverPath),
|
|
191
|
+
context.loggerPath,
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
// Generate import statements
|
|
195
|
+
const imports = Array.from(importsByFile.entries())
|
|
196
|
+
.map(
|
|
197
|
+
([importPath, exports]) =>
|
|
198
|
+
`import { ${exports.join(', ')} } from '${importPath}';`,
|
|
199
|
+
)
|
|
200
|
+
.join('\n');
|
|
201
|
+
|
|
202
|
+
const allExportNames = endpoints.map(({ key }) => key);
|
|
203
|
+
|
|
204
|
+
const content = `import { HonoEndpoint } from '@geekmidas/api/hono';
|
|
205
|
+
import { Endpoint } from '@geekmidas/api/server';
|
|
206
|
+
import { ServiceDiscovery } from '@geekmidas/api/services';
|
|
207
|
+
import { Hono } from 'hono';
|
|
208
|
+
import ${context.envParserImportPattern} from '${relativeEnvParserPath}';
|
|
209
|
+
import ${context.loggerImportPattern} from '${relativeLoggerPath}';
|
|
210
|
+
${imports}
|
|
211
|
+
|
|
212
|
+
export function createApp(app?: Hono, enableOpenApi: boolean = ${enableOpenApi}): Hono {
|
|
213
|
+
const honoApp = app || new Hono();
|
|
214
|
+
|
|
215
|
+
const endpoints: Endpoint<any, any, any, any, any, any, any>[] = [
|
|
216
|
+
${allExportNames.join(',\n ')}
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
const serviceDiscovery = ServiceDiscovery.getInstance(
|
|
220
|
+
logger,
|
|
221
|
+
envParser
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// Configure OpenAPI options based on enableOpenApi flag
|
|
225
|
+
const openApiOptions: any = enableOpenApi ? {
|
|
226
|
+
docsPath: '/docs',
|
|
227
|
+
openApiOptions: {
|
|
228
|
+
title: 'API Documentation',
|
|
229
|
+
version: '1.0.0',
|
|
230
|
+
description: 'Generated API documentation'
|
|
231
|
+
}
|
|
232
|
+
} : { docsPath: false };
|
|
233
|
+
|
|
234
|
+
HonoEndpoint.addRoutes(endpoints, serviceDiscovery, honoApp, openApiOptions);
|
|
235
|
+
|
|
236
|
+
return honoApp;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Default export for convenience
|
|
240
|
+
export default createApp;
|
|
241
|
+
`;
|
|
242
|
+
|
|
243
|
+
await writeFile(serverPath, content);
|
|
244
|
+
|
|
245
|
+
return serverPath;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private generateAWSApiGatewayV1Handler(
|
|
249
|
+
importPath: string,
|
|
250
|
+
exportName: string,
|
|
251
|
+
envParserPath: string,
|
|
252
|
+
envParserImportPattern: string,
|
|
253
|
+
): string {
|
|
254
|
+
return `import { AmazonApiGatewayV1Endpoint } from '@geekmidas/api/aws-apigateway';
|
|
255
|
+
import { ${exportName} } from '${importPath}';
|
|
256
|
+
import ${envParserImportPattern} from '${envParserPath}';
|
|
257
|
+
|
|
258
|
+
const adapter = new AmazonApiGatewayV1Endpoint(envParser, ${exportName});
|
|
259
|
+
|
|
260
|
+
export const handler = adapter.handler;
|
|
261
|
+
`;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private generateAWSApiGatewayV2Handler(
|
|
265
|
+
importPath: string,
|
|
266
|
+
exportName: string,
|
|
267
|
+
envParserPath: string,
|
|
268
|
+
envParserImportPattern: string,
|
|
269
|
+
): string {
|
|
270
|
+
return `import { AmazonApiGatewayV2Endpoint } from '@geekmidas/api/aws-apigateway';
|
|
271
|
+
import { ${exportName} } from '${importPath}';
|
|
272
|
+
import ${envParserImportPattern} from '${envParserPath}';
|
|
273
|
+
|
|
274
|
+
const adapter = new AmazonApiGatewayV2Endpoint(envParser, ${exportName});
|
|
275
|
+
|
|
276
|
+
export const handler = adapter.handler;
|
|
277
|
+
`;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
private generateServerHandler(
|
|
281
|
+
importPath: string,
|
|
282
|
+
exportName: string,
|
|
283
|
+
): string {
|
|
284
|
+
return `import { ${exportName} } from '${importPath}';
|
|
285
|
+
|
|
286
|
+
// Server handler - implement based on your server framework
|
|
287
|
+
export const handler = ${exportName};
|
|
288
|
+
`;
|
|
289
|
+
}
|
|
290
|
+
}
|