@geekmidas/cli 0.0.26 → 0.2.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 (121) hide show
  1. package/FUNCTION_CRON_SUPPORT.md +266 -0
  2. package/README.md +84 -17
  3. package/dist/CronGenerator-1PflEYe2.cjs +60 -0
  4. package/dist/CronGenerator-1PflEYe2.cjs.map +1 -0
  5. package/dist/CronGenerator-DXRfHQcV.mjs +54 -0
  6. package/dist/CronGenerator-DXRfHQcV.mjs.map +1 -0
  7. package/dist/EndpointGenerator-BbGrDiCP.cjs +264 -0
  8. package/dist/EndpointGenerator-BbGrDiCP.cjs.map +1 -0
  9. package/dist/EndpointGenerator-BmZ9BxbO.mjs +258 -0
  10. package/dist/EndpointGenerator-BmZ9BxbO.mjs.map +1 -0
  11. package/dist/FunctionGenerator-Clw64SwQ.cjs +59 -0
  12. package/dist/FunctionGenerator-Clw64SwQ.cjs.map +1 -0
  13. package/dist/FunctionGenerator-DOEB_yPh.mjs +53 -0
  14. package/dist/FunctionGenerator-DOEB_yPh.mjs.map +1 -0
  15. package/dist/Generator-CDoEXCDg.cjs +47 -0
  16. package/dist/Generator-CDoEXCDg.cjs.map +1 -0
  17. package/dist/Generator-UanJW0_V.mjs +41 -0
  18. package/dist/Generator-UanJW0_V.mjs.map +1 -0
  19. package/dist/SubscriberGenerator-BfMZCVNy.cjs +204 -0
  20. package/dist/SubscriberGenerator-BfMZCVNy.cjs.map +1 -0
  21. package/dist/SubscriberGenerator-D2u00NI3.mjs +198 -0
  22. package/dist/SubscriberGenerator-D2u00NI3.mjs.map +1 -0
  23. package/dist/build/index.cjs +12 -0
  24. package/dist/build/index.mjs +12 -0
  25. package/dist/build/manifests.cjs +3 -0
  26. package/dist/build/manifests.mjs +3 -0
  27. package/dist/build/providerResolver.cjs +5 -0
  28. package/dist/build/providerResolver.mjs +3 -0
  29. package/dist/build/types.cjs +0 -0
  30. package/dist/build/types.mjs +0 -0
  31. package/dist/build-BBhlEjf5.cjs +89 -0
  32. package/dist/build-BBhlEjf5.cjs.map +1 -0
  33. package/dist/build-kY-lG30Q.mjs +83 -0
  34. package/dist/build-kY-lG30Q.mjs.map +1 -0
  35. package/dist/config-D1EpSGk6.cjs +36 -0
  36. package/dist/config-D1EpSGk6.cjs.map +1 -0
  37. package/dist/config-U-mdW-7Y.mjs +30 -0
  38. package/dist/config-U-mdW-7Y.mjs.map +1 -0
  39. package/dist/config.cjs +1 -1
  40. package/dist/config.mjs +1 -1
  41. package/dist/generators/CronGenerator.cjs +4 -0
  42. package/dist/generators/CronGenerator.mjs +4 -0
  43. package/dist/generators/EndpointGenerator.cjs +4 -0
  44. package/dist/generators/EndpointGenerator.mjs +4 -0
  45. package/dist/generators/FunctionGenerator.cjs +4 -0
  46. package/dist/generators/FunctionGenerator.mjs +4 -0
  47. package/dist/generators/Generator.cjs +3 -0
  48. package/dist/generators/Generator.mjs +3 -0
  49. package/dist/generators/SubscriberGenerator.cjs +4 -0
  50. package/dist/generators/SubscriberGenerator.mjs +4 -0
  51. package/dist/generators/index.cjs +12 -0
  52. package/dist/generators/index.mjs +8 -0
  53. package/dist/generators-CEKtVh81.cjs +0 -0
  54. package/dist/generators-CsLujGXs.mjs +0 -0
  55. package/dist/index.cjs +71 -25
  56. package/dist/index.cjs.map +1 -0
  57. package/dist/index.mjs +71 -25
  58. package/dist/index.mjs.map +1 -0
  59. package/dist/manifests-BrJXpHrf.mjs +21 -0
  60. package/dist/manifests-BrJXpHrf.mjs.map +1 -0
  61. package/dist/manifests-D0saShvH.cjs +27 -0
  62. package/dist/manifests-D0saShvH.cjs.map +1 -0
  63. package/dist/{openapi-CksVdkh2.mjs → openapi-BQx3_JbM.mjs} +8 -6
  64. package/dist/openapi-BQx3_JbM.mjs.map +1 -0
  65. package/dist/{openapi-D4QQJUPY.cjs → openapi-CMLr04cz.cjs} +9 -7
  66. package/dist/openapi-CMLr04cz.cjs.map +1 -0
  67. package/dist/{openapi-react-query-DpT3XHFC.mjs → openapi-react-query-DbrWwQzb.mjs} +5 -3
  68. package/dist/openapi-react-query-DbrWwQzb.mjs.map +1 -0
  69. package/dist/{openapi-react-query-C1JLYUOs.cjs → openapi-react-query-Dvjqx_Eo.cjs} +5 -3
  70. package/dist/openapi-react-query-Dvjqx_Eo.cjs.map +1 -0
  71. package/dist/openapi-react-query.cjs +1 -1
  72. package/dist/openapi-react-query.mjs +1 -1
  73. package/dist/openapi.cjs +4 -3
  74. package/dist/openapi.mjs +4 -3
  75. package/dist/providerResolver-B_TjNF0_.mjs +96 -0
  76. package/dist/providerResolver-B_TjNF0_.mjs.map +1 -0
  77. package/dist/providerResolver-DgvzNfP4.cjs +114 -0
  78. package/dist/providerResolver-DgvzNfP4.cjs.map +1 -0
  79. package/examples/cron-example.ts +45 -0
  80. package/examples/function-example.ts +40 -0
  81. package/examples/gkm.config.json +22 -0
  82. package/examples/gkm.minimal.config.json +7 -0
  83. package/examples/gkm.production.config.json +27 -0
  84. package/examples/logger.ts +1 -1
  85. package/package.json +38 -14
  86. package/src/__tests__/config.spec.ts +110 -0
  87. package/src/__tests__/openapi-react-query.spec.ts +506 -0
  88. package/src/__tests__/openapi.spec.ts +362 -0
  89. package/src/__tests__/test-helpers.ts +180 -0
  90. package/src/build/__tests__/index-new.spec.ts +577 -0
  91. package/src/build/index.ts +197 -0
  92. package/src/build/manifests.ts +35 -0
  93. package/src/build/providerResolver.ts +184 -0
  94. package/src/build/types.ts +37 -0
  95. package/src/config.ts +14 -6
  96. package/src/generators/CronGenerator.ts +98 -0
  97. package/src/generators/EndpointGenerator.ts +389 -0
  98. package/src/generators/FunctionGenerator.ts +97 -0
  99. package/src/generators/Generator.ts +95 -0
  100. package/src/generators/SubscriberGenerator.ts +271 -0
  101. package/src/generators/__tests__/CronGenerator.spec.ts +445 -0
  102. package/src/generators/__tests__/EndpointGenerator.spec.ts +394 -0
  103. package/src/generators/__tests__/FunctionGenerator.spec.ts +256 -0
  104. package/src/generators/__tests__/SubscriberGenerator.spec.ts +341 -0
  105. package/src/generators/index.ts +9 -0
  106. package/src/index.ts +57 -22
  107. package/src/openapi-react-query.ts +2 -1
  108. package/src/openapi.ts +5 -4
  109. package/src/types.ts +91 -2
  110. package/dist/build-BTggTCYL.cjs +0 -176
  111. package/dist/build-Ca4P6_lY.mjs +0 -170
  112. package/dist/build.cjs +0 -5
  113. package/dist/build.mjs +0 -5
  114. package/dist/config-BNqUMsvc.cjs +0 -24
  115. package/dist/config-BciAdY6_.mjs +0 -18
  116. package/dist/loadEndpoints-BBIavB9h.cjs +0 -37
  117. package/dist/loadEndpoints-DAZ53Og2.mjs +0 -31
  118. package/dist/loadEndpoints.cjs +0 -3
  119. package/dist/loadEndpoints.mjs +0 -3
  120. package/src/build.ts +0 -305
  121. package/src/loadEndpoints.ts +0 -48
@@ -0,0 +1,197 @@
1
+ import { mkdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import type { Cron } from '@geekmidas/constructs/crons';
4
+ import type { Endpoint } from '@geekmidas/constructs/endpoints';
5
+ import type { Function } from '@geekmidas/constructs/functions';
6
+ import type { Subscriber } from '@geekmidas/constructs/subscribers';
7
+ import { loadConfig } from '../config';
8
+ import {
9
+ CronGenerator,
10
+ EndpointGenerator,
11
+ FunctionGenerator,
12
+ type GeneratedConstruct,
13
+ SubscriberGenerator,
14
+ } from '../generators';
15
+ import type {
16
+ BuildOptions,
17
+ CronInfo,
18
+ FunctionInfo,
19
+ LegacyProvider,
20
+ RouteInfo,
21
+ SubscriberInfo,
22
+ } from '../types';
23
+ import { generateManifests } from './manifests';
24
+ import { resolveProviders } from './providerResolver';
25
+ import type { BuildContext } from './types';
26
+
27
+ const logger = console;
28
+
29
+ export async function buildCommand(options: BuildOptions): Promise<void> {
30
+ const config = await loadConfig();
31
+
32
+ // Resolve providers from new config format
33
+ const resolved = resolveProviders(config, options);
34
+
35
+ logger.log(`Building with providers: ${resolved.providers.join(', ')}`);
36
+ logger.log(`Loading routes from: ${config.routes}`);
37
+ if (config.functions) {
38
+ logger.log(`Loading functions from: ${config.functions}`);
39
+ }
40
+ if (config.crons) {
41
+ logger.log(`Loading crons from: ${config.crons}`);
42
+ }
43
+ if (config.subscribers) {
44
+ logger.log(`Loading subscribers from: ${config.subscribers}`);
45
+ }
46
+ logger.log(`Using envParser: ${config.envParser}`);
47
+
48
+ // Parse envParser configuration
49
+ const [envParserPath, envParserName] = config.envParser.split('#');
50
+ const envParserImportPattern = !envParserName
51
+ ? 'envParser'
52
+ : envParserName === 'envParser'
53
+ ? '{ envParser }'
54
+ : `{ ${envParserName} as envParser }`;
55
+
56
+ // Parse logger configuration
57
+ const [loggerPath, loggerName] = config.logger.split('#');
58
+ const loggerImportPattern = !loggerName
59
+ ? 'logger'
60
+ : loggerName === 'logger'
61
+ ? '{ logger }'
62
+ : `{ ${loggerName} as logger }`;
63
+
64
+ const buildContext: BuildContext = {
65
+ envParserPath,
66
+ envParserImportPattern,
67
+ loggerPath,
68
+ loggerImportPattern,
69
+ };
70
+
71
+ // Initialize generators
72
+ const endpointGenerator = new EndpointGenerator();
73
+ const functionGenerator = new FunctionGenerator();
74
+ const cronGenerator = new CronGenerator();
75
+ const subscriberGenerator = new SubscriberGenerator();
76
+
77
+ // Load all constructs in parallel
78
+ const [allEndpoints, allFunctions, allCrons, allSubscribers] =
79
+ await Promise.all([
80
+ endpointGenerator.load(config.routes),
81
+ config.functions ? functionGenerator.load(config.functions) : [],
82
+ config.crons ? cronGenerator.load(config.crons) : [],
83
+ config.subscribers ? subscriberGenerator.load(config.subscribers) : [],
84
+ ]);
85
+
86
+ logger.log(`Found ${allEndpoints.length} endpoints`);
87
+ logger.log(`Found ${allFunctions.length} functions`);
88
+ logger.log(`Found ${allCrons.length} crons`);
89
+ logger.log(`Found ${allSubscribers.length} subscribers`);
90
+
91
+ if (
92
+ allEndpoints.length === 0 &&
93
+ allFunctions.length === 0 &&
94
+ allCrons.length === 0 &&
95
+ allSubscribers.length === 0
96
+ ) {
97
+ logger.log(
98
+ 'No endpoints, functions, crons, or subscribers found to process',
99
+ );
100
+ return;
101
+ }
102
+
103
+ // Ensure .gkm directory exists
104
+ const rootOutputDir = join(process.cwd(), '.gkm');
105
+ await mkdir(rootOutputDir, { recursive: true });
106
+
107
+ // Collect all build results from each provider
108
+ const allBuildResults = await Promise.all(
109
+ resolved.providers.map((provider) =>
110
+ buildForProvider(
111
+ provider,
112
+ buildContext,
113
+ endpointGenerator,
114
+ functionGenerator,
115
+ cronGenerator,
116
+ subscriberGenerator,
117
+ allEndpoints,
118
+ allFunctions,
119
+ allCrons,
120
+ allSubscribers,
121
+ resolved.enableOpenApi,
122
+ ),
123
+ ),
124
+ );
125
+
126
+ // Aggregate all routes, functions, crons, and subscribers from all providers
127
+ const aggregatedRoutes = allBuildResults.flatMap((result) => result.routes);
128
+ const aggregatedFunctions = allBuildResults.flatMap(
129
+ (result) => result.functions,
130
+ );
131
+ const aggregatedCrons = allBuildResults.flatMap((result) => result.crons);
132
+ const aggregatedSubscribers = allBuildResults.flatMap(
133
+ (result) => result.subscribers,
134
+ );
135
+
136
+ // Generate single manifest at root .gkm directory
137
+ await generateManifests(
138
+ rootOutputDir,
139
+ aggregatedRoutes,
140
+ aggregatedFunctions,
141
+ aggregatedCrons,
142
+ aggregatedSubscribers,
143
+ );
144
+ }
145
+
146
+ interface BuildResult {
147
+ routes: RouteInfo[];
148
+ functions: FunctionInfo[];
149
+ crons: CronInfo[];
150
+ subscribers: SubscriberInfo[];
151
+ }
152
+
153
+ async function buildForProvider(
154
+ provider: LegacyProvider,
155
+ context: BuildContext,
156
+ endpointGenerator: EndpointGenerator,
157
+ functionGenerator: FunctionGenerator,
158
+ cronGenerator: CronGenerator,
159
+ subscriberGenerator: SubscriberGenerator,
160
+ endpoints: GeneratedConstruct<Endpoint<any, any, any, any, any, any>>[],
161
+ functions: GeneratedConstruct<Function<any, any, any, any>>[],
162
+ crons: GeneratedConstruct<Cron<any, any, any, any>>[],
163
+ subscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[],
164
+ enableOpenApi: boolean,
165
+ ): Promise<BuildResult> {
166
+ const outputDir = join(process.cwd(), '.gkm', provider);
167
+
168
+ // Ensure output directory exists
169
+ await mkdir(outputDir, { recursive: true });
170
+
171
+ logger.log(`\nGenerating handlers for provider: ${provider}`);
172
+
173
+ // Build all constructs in parallel
174
+ const [routes, functionInfos, cronInfos, subscriberInfos] = await Promise.all(
175
+ [
176
+ endpointGenerator.build(context, endpoints, outputDir, {
177
+ provider,
178
+ enableOpenApi,
179
+ }),
180
+ functionGenerator.build(context, functions, outputDir, { provider }),
181
+ cronGenerator.build(context, crons, outputDir, { provider }),
182
+ subscriberGenerator.build(context, subscribers, outputDir, { provider }),
183
+ ],
184
+ );
185
+
186
+ logger.log(
187
+ `Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers for ${provider}`,
188
+ );
189
+
190
+ // Return build results instead of generating manifest here
191
+ return {
192
+ routes,
193
+ functions: functionInfos,
194
+ crons: cronInfos,
195
+ subscribers: subscriberInfos,
196
+ };
197
+ }
@@ -0,0 +1,35 @@
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
+ SubscriberInfo,
9
+ } from '../types';
10
+
11
+ const logger = console;
12
+
13
+ export async function generateManifests(
14
+ outputDir: string,
15
+ routes: RouteInfo[],
16
+ functions: FunctionInfo[],
17
+ crons: CronInfo[],
18
+ subscribers: SubscriberInfo[],
19
+ ): Promise<void> {
20
+ // Generate unified manifest for all providers
21
+ const manifest: BuildManifest = {
22
+ routes,
23
+ functions,
24
+ crons,
25
+ subscribers,
26
+ };
27
+
28
+ const manifestPath = join(outputDir, 'manifest.json');
29
+ await writeFile(manifestPath, JSON.stringify(manifest, null, 2));
30
+
31
+ logger.log(
32
+ `\nGenerated unified manifest with ${routes.length} routes, ${functions.length} functions, ${crons.length} crons, ${subscribers.length} subscribers`,
33
+ );
34
+ logger.log(`Manifest: ${relative(process.cwd(), manifestPath)}`);
35
+ }
@@ -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/constructs';
2
+ import type { Endpoint } from '@geekmidas/constructs';
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 configPath = join(process.cwd(), 'gkm.config.json');
6
+ const files = ['gkm.config.json', 'gkm.config.ts', 'gkm.config.js'];
7
+ let configPath = '';
8
8
 
9
- if (!existsSync(configPath)) {
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
- 'gkm.config.json not found. Please create a configuration file.',
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 readFile(configPath, 'utf-8');
17
- return JSON.parse(config);
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,98 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { dirname, join, relative } from 'node:path';
3
+ import { Cron } from '@geekmidas/constructs/crons';
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
+ environment: await construct.getEnvironment(),
52
+ });
53
+
54
+ logger.log(`Generated cron handler: ${key}`);
55
+ }
56
+
57
+ return cronInfos;
58
+ }
59
+
60
+ isConstruct(value: any): value is Cron<any, any, any, any> {
61
+ return Cron.isCron(value);
62
+ }
63
+
64
+ private async generateCronHandler(
65
+ outputDir: string,
66
+ sourceFile: string,
67
+ exportName: string,
68
+ context: BuildContext,
69
+ ): Promise<string> {
70
+ const handlerFileName = `${exportName}.ts`;
71
+ const handlerPath = join(outputDir, handlerFileName);
72
+
73
+ const relativePath = relative(dirname(handlerPath), sourceFile);
74
+ const importPath = relativePath.replace(/\.ts$/, '.js');
75
+
76
+ const relativeEnvParserPath = relative(
77
+ dirname(handlerPath),
78
+ context.envParserPath,
79
+ );
80
+ const relativeLoggerPath = relative(
81
+ dirname(handlerPath),
82
+ context.loggerPath,
83
+ );
84
+
85
+ const content = `import { AWSScheduledFunction } from '@geekmidas/constructs/crons';
86
+ import { ${exportName} } from '${importPath}';
87
+ import ${context.envParserImportPattern} from '${relativeEnvParserPath}';
88
+ import ${context.loggerImportPattern} from '${relativeLoggerPath}';
89
+
90
+ const adapter = new AWSScheduledFunction(envParser, ${exportName});
91
+
92
+ export const handler = adapter.handler;
93
+ `;
94
+
95
+ await writeFile(handlerPath, content);
96
+ return handlerPath;
97
+ }
98
+ }