@geekmidas/cli 0.17.0 → 0.19.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/dist/{bundler-C74EKlNa.cjs → bundler-CyHg1v_T.cjs} +3 -3
- package/dist/{bundler-C74EKlNa.cjs.map → bundler-CyHg1v_T.cjs.map} +1 -1
- package/dist/{bundler-B6z6HEeh.mjs → bundler-DQIuE3Kn.mjs} +3 -3
- package/dist/{bundler-B6z6HEeh.mjs.map → bundler-DQIuE3Kn.mjs.map} +1 -1
- package/dist/{config-DYULeEv8.mjs → config-BaYqrF3n.mjs} +48 -10
- package/dist/config-BaYqrF3n.mjs.map +1 -0
- package/dist/{config-AmInkU7k.cjs → config-CxrLu8ia.cjs} +53 -9
- package/dist/config-CxrLu8ia.cjs.map +1 -0
- package/dist/config.cjs +4 -1
- package/dist/config.d.cts +27 -2
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts +27 -2
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +3 -2
- package/dist/dokploy-api-B0w17y4_.mjs +3 -0
- package/dist/{dokploy-api-CaETb2L6.mjs → dokploy-api-B9qR2Yn1.mjs} +1 -1
- package/dist/{dokploy-api-CaETb2L6.mjs.map → dokploy-api-B9qR2Yn1.mjs.map} +1 -1
- package/dist/dokploy-api-BnGeUqN4.cjs +3 -0
- package/dist/{dokploy-api-C7F9VykY.cjs → dokploy-api-C5czOZoc.cjs} +1 -1
- package/dist/{dokploy-api-C7F9VykY.cjs.map → dokploy-api-C5czOZoc.cjs.map} +1 -1
- package/dist/{encryption-D7Efcdi9.cjs → encryption-BAz0xQ1Q.cjs} +1 -1
- package/dist/{encryption-D7Efcdi9.cjs.map → encryption-BAz0xQ1Q.cjs.map} +1 -1
- package/dist/{encryption-h4Nb6W-M.mjs → encryption-JtMsiGNp.mjs} +2 -2
- package/dist/{encryption-h4Nb6W-M.mjs.map → encryption-JtMsiGNp.mjs.map} +1 -1
- package/dist/index-CWN-bgrO.d.mts +495 -0
- package/dist/index-CWN-bgrO.d.mts.map +1 -0
- package/dist/index-DEWYvYvg.d.cts +495 -0
- package/dist/index-DEWYvYvg.d.cts.map +1 -0
- package/dist/index.cjs +2644 -564
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +2639 -564
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-CZVcfxk-.mjs → openapi-CgqR6Jkw.mjs} +3 -3
- package/dist/{openapi-CZVcfxk-.mjs.map → openapi-CgqR6Jkw.mjs.map} +1 -1
- package/dist/{openapi-C89hhkZC.cjs → openapi-DfpxS0xv.cjs} +8 -2
- package/dist/{openapi-C89hhkZC.cjs.map → openapi-DfpxS0xv.cjs.map} +1 -1
- package/dist/{openapi-react-query-CM2_qlW9.mjs → openapi-react-query-5rSortLH.mjs} +1 -1
- package/dist/{openapi-react-query-CM2_qlW9.mjs.map → openapi-react-query-5rSortLH.mjs.map} +1 -1
- package/dist/{openapi-react-query-iKjfLzff.cjs → openapi-react-query-DvNpdDpM.cjs} +1 -1
- package/dist/{openapi-react-query-iKjfLzff.cjs.map → openapi-react-query-DvNpdDpM.cjs.map} +1 -1
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.cjs +3 -2
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +3 -2
- package/dist/{storage-Bn3K9Ccu.cjs → storage-BPRgh3DU.cjs} +136 -5
- package/dist/storage-BPRgh3DU.cjs.map +1 -0
- package/dist/{storage-nkGIjeXt.mjs → storage-DNj_I11J.mjs} +1 -1
- package/dist/storage-Dhst7BhI.mjs +272 -0
- package/dist/storage-Dhst7BhI.mjs.map +1 -0
- package/dist/{storage-UfyTn7Zm.cjs → storage-fOR8dMu5.cjs} +1 -1
- package/dist/{types-iFk5ms7y.d.mts → types-K2uQJ-FO.d.mts} +2 -2
- package/dist/{types-BgaMXsUa.d.cts.map → types-K2uQJ-FO.d.mts.map} +1 -1
- package/dist/{types-BgaMXsUa.d.cts → types-l53qUmGt.d.cts} +2 -2
- package/dist/{types-iFk5ms7y.d.mts.map → types-l53qUmGt.d.cts.map} +1 -1
- package/dist/workspace/index.cjs +19 -0
- package/dist/workspace/index.d.cts +3 -0
- package/dist/workspace/index.d.mts +3 -0
- package/dist/workspace/index.mjs +3 -0
- package/dist/workspace-CPLEZDZf.mjs +3788 -0
- package/dist/workspace-CPLEZDZf.mjs.map +1 -0
- package/dist/workspace-iWgBlX6h.cjs +3885 -0
- package/dist/workspace-iWgBlX6h.cjs.map +1 -0
- package/package.json +9 -4
- package/src/build/__tests__/workspace-build.spec.ts +215 -0
- package/src/build/index.ts +189 -1
- package/src/config.ts +71 -14
- package/src/deploy/__tests__/docker.spec.ts +1 -1
- package/src/deploy/__tests__/index.spec.ts +305 -1
- package/src/deploy/index.ts +426 -4
- package/src/deploy/types.ts +32 -0
- package/src/dev/__tests__/index.spec.ts +572 -1
- package/src/dev/index.ts +582 -2
- package/src/docker/__tests__/compose.spec.ts +425 -0
- package/src/docker/__tests__/templates.spec.ts +145 -0
- package/src/docker/compose.ts +248 -0
- package/src/docker/index.ts +159 -3
- package/src/docker/templates.ts +223 -4
- package/src/index.ts +24 -0
- package/src/init/__tests__/generators.spec.ts +17 -24
- package/src/init/__tests__/init.spec.ts +157 -5
- package/src/init/generators/auth.ts +220 -0
- package/src/init/generators/config.ts +61 -4
- package/src/init/generators/docker.ts +115 -8
- package/src/init/generators/env.ts +7 -127
- package/src/init/generators/index.ts +1 -0
- package/src/init/generators/models.ts +3 -1
- package/src/init/generators/monorepo.ts +154 -10
- package/src/init/generators/package.ts +5 -3
- package/src/init/generators/web.ts +213 -0
- package/src/init/index.ts +290 -58
- package/src/init/templates/api.ts +38 -29
- package/src/init/templates/index.ts +132 -4
- package/src/init/templates/minimal.ts +33 -35
- package/src/init/templates/serverless.ts +16 -19
- package/src/init/templates/worker.ts +50 -25
- package/src/init/versions.ts +47 -0
- package/src/secrets/keystore.ts +144 -0
- package/src/secrets/storage.ts +109 -6
- package/src/test/index.ts +97 -0
- package/src/workspace/__tests__/client-generator.spec.ts +357 -0
- package/src/workspace/__tests__/index.spec.ts +543 -0
- package/src/workspace/__tests__/schema.spec.ts +519 -0
- package/src/workspace/__tests__/type-inference.spec.ts +251 -0
- package/src/workspace/client-generator.ts +307 -0
- package/src/workspace/index.ts +372 -0
- package/src/workspace/schema.ts +368 -0
- package/src/workspace/types.ts +336 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsdown.config.ts +1 -0
- package/dist/config-AmInkU7k.cjs.map +0 -1
- package/dist/config-DYULeEv8.mjs.map +0 -1
- package/dist/dokploy-api-B7KxOQr3.cjs +0 -3
- package/dist/dokploy-api-DHvfmWbi.mjs +0 -3
- package/dist/storage-BaOP55oq.mjs +0 -147
- package/dist/storage-BaOP55oq.mjs.map +0 -1
- package/dist/storage-Bn3K9Ccu.cjs.map +0 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { GEEKMIDAS_VERSIONS } from '../versions.js';
|
|
1
2
|
import type {
|
|
2
3
|
GeneratedFile,
|
|
3
4
|
TemplateConfig,
|
|
@@ -9,19 +10,19 @@ export const apiTemplate: TemplateConfig = {
|
|
|
9
10
|
description: 'Full API with auth, database, services',
|
|
10
11
|
|
|
11
12
|
dependencies: {
|
|
12
|
-
'@geekmidas/constructs': '
|
|
13
|
-
'@geekmidas/envkit': '
|
|
14
|
-
'@geekmidas/logger': '
|
|
15
|
-
'@geekmidas/services': '
|
|
16
|
-
'@geekmidas/errors': '
|
|
17
|
-
'@geekmidas/auth': '
|
|
13
|
+
'@geekmidas/constructs': GEEKMIDAS_VERSIONS['@geekmidas/constructs'],
|
|
14
|
+
'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],
|
|
15
|
+
'@geekmidas/logger': GEEKMIDAS_VERSIONS['@geekmidas/logger'],
|
|
16
|
+
'@geekmidas/services': GEEKMIDAS_VERSIONS['@geekmidas/services'],
|
|
17
|
+
'@geekmidas/errors': GEEKMIDAS_VERSIONS['@geekmidas/errors'],
|
|
18
|
+
'@geekmidas/auth': GEEKMIDAS_VERSIONS['@geekmidas/auth'],
|
|
18
19
|
hono: '~4.8.2',
|
|
19
20
|
pino: '~9.6.0',
|
|
20
21
|
},
|
|
21
22
|
|
|
22
23
|
devDependencies: {
|
|
23
|
-
'@biomejs/biome': '~
|
|
24
|
-
'@geekmidas/cli': '
|
|
24
|
+
'@biomejs/biome': '~2.3.0',
|
|
25
|
+
'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],
|
|
25
26
|
'@types/node': '~22.0.0',
|
|
26
27
|
tsx: '~4.20.0',
|
|
27
28
|
turbo: '~2.3.0',
|
|
@@ -69,22 +70,17 @@ export const logger = createLogger();
|
|
|
69
70
|
// src/config/env.ts
|
|
70
71
|
{
|
|
71
72
|
path: 'src/config/env.ts',
|
|
72
|
-
content: `import {
|
|
73
|
+
content: `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
74
|
+
import { EnvironmentParser } from '@geekmidas/envkit';
|
|
73
75
|
|
|
74
|
-
export const envParser = new EnvironmentParser(process.env);
|
|
76
|
+
export const envParser = new EnvironmentParser({ ...process.env, ...Credentials });
|
|
75
77
|
|
|
78
|
+
// Global config - only minimal shared values
|
|
79
|
+
// Service-specific config should be parsed in each service
|
|
76
80
|
export const config = envParser
|
|
77
81
|
.create((get) => ({
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
jwtSecret: get('JWT_SECRET').string().default('change-me-in-production'),${
|
|
81
|
-
options.database
|
|
82
|
-
? `
|
|
83
|
-
database: {
|
|
84
|
-
url: get('DATABASE_URL').string().default('postgresql://localhost:5432/mydb'),
|
|
85
|
-
},`
|
|
86
|
-
: ''
|
|
87
|
-
}
|
|
82
|
+
nodeEnv: get('NODE_ENV').enum(['development', 'test', 'production']).default('development'),
|
|
83
|
+
stage: get('STAGE').enum(['development', 'staging', 'production']).default('development'),
|
|
88
84
|
}))
|
|
89
85
|
.parse();
|
|
90
86
|
`,
|
|
@@ -101,7 +97,7 @@ export const config = envParser
|
|
|
101
97
|
path: getRoutePath('health.ts'),
|
|
102
98
|
content: `import { e } from '@geekmidas/constructs/endpoints';
|
|
103
99
|
|
|
104
|
-
export
|
|
100
|
+
export const healthEndpoint = e
|
|
105
101
|
.get('/health')
|
|
106
102
|
.handle(async () => ({
|
|
107
103
|
status: 'ok',
|
|
@@ -115,7 +111,7 @@ export default e
|
|
|
115
111
|
path: getRoutePath('users/list.ts'),
|
|
116
112
|
content: `import { e } from '@geekmidas/constructs/endpoints';
|
|
117
113
|
|
|
118
|
-
export
|
|
114
|
+
export const listUsersEndpoint = e
|
|
119
115
|
.get('/users')
|
|
120
116
|
.handle(async () => ({
|
|
121
117
|
users: [
|
|
@@ -130,7 +126,7 @@ export default e
|
|
|
130
126
|
content: `import { e } from '@geekmidas/constructs/endpoints';
|
|
131
127
|
import { z } from 'zod';
|
|
132
128
|
|
|
133
|
-
export
|
|
129
|
+
export const getUserEndpoint = e
|
|
134
130
|
.get('/users/:id')
|
|
135
131
|
.params(z.object({ id: z.string() }))
|
|
136
132
|
.handle(async ({ params }) => ({
|
|
@@ -146,7 +142,7 @@ export default e
|
|
|
146
142
|
if (options.database) {
|
|
147
143
|
files.push({
|
|
148
144
|
path: 'src/services/database.ts',
|
|
149
|
-
content: `import type { Service } from '@geekmidas/services';
|
|
145
|
+
content: `import type { Service, ServiceRegisterOptions } from '@geekmidas/services';
|
|
150
146
|
import { Kysely, PostgresDialect } from 'kysely';
|
|
151
147
|
import pg from 'pg';
|
|
152
148
|
|
|
@@ -162,18 +158,24 @@ export interface Database {
|
|
|
162
158
|
|
|
163
159
|
export const databaseService = {
|
|
164
160
|
serviceName: 'database' as const,
|
|
165
|
-
async register(envParser) {
|
|
161
|
+
async register({ envParser, context }: ServiceRegisterOptions) {
|
|
162
|
+
const logger = context.getLogger();
|
|
163
|
+
logger.info('Connecting to database');
|
|
164
|
+
|
|
166
165
|
const config = envParser
|
|
167
166
|
.create((get) => ({
|
|
168
167
|
url: get('DATABASE_URL').string(),
|
|
169
168
|
}))
|
|
170
169
|
.parse();
|
|
171
170
|
|
|
172
|
-
|
|
171
|
+
const db = new Kysely<Database>({
|
|
173
172
|
dialect: new PostgresDialect({
|
|
174
173
|
pool: new pg.Pool({ connectionString: config.url }),
|
|
175
174
|
}),
|
|
176
175
|
});
|
|
176
|
+
|
|
177
|
+
logger.info('Database connection established');
|
|
178
|
+
return db;
|
|
177
179
|
},
|
|
178
180
|
} satisfies Service<'database', Kysely<Database>>;
|
|
179
181
|
`,
|
|
@@ -202,13 +204,20 @@ export const telescope = new Telescope({
|
|
|
202
204
|
content: `import { Direction, InMemoryMonitoringStorage, Studio } from '@geekmidas/studio';
|
|
203
205
|
import { Kysely, PostgresDialect } from 'kysely';
|
|
204
206
|
import pg from 'pg';
|
|
205
|
-
import type { Database } from '../services/database';
|
|
206
|
-
import {
|
|
207
|
+
import type { Database } from '../services/database.js';
|
|
208
|
+
import { envParser } from './env.js';
|
|
209
|
+
|
|
210
|
+
// Parse database config for Studio
|
|
211
|
+
const studioConfig = envParser
|
|
212
|
+
.create((get) => ({
|
|
213
|
+
databaseUrl: get('DATABASE_URL').string(),
|
|
214
|
+
}))
|
|
215
|
+
.parse();
|
|
207
216
|
|
|
208
217
|
// Create a Kysely instance for Studio
|
|
209
218
|
const db = new Kysely<Database>({
|
|
210
219
|
dialect: new PostgresDialect({
|
|
211
|
-
pool: new pg.Pool({ connectionString:
|
|
220
|
+
pool: new pg.Pool({ connectionString: studioConfig.databaseUrl }),
|
|
212
221
|
}),
|
|
213
222
|
});
|
|
214
223
|
|
|
@@ -21,6 +21,25 @@ export type RoutesStructure =
|
|
|
21
21
|
| 'centralized-routes'
|
|
22
22
|
| 'domain-based';
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Package manager type
|
|
26
|
+
*/
|
|
27
|
+
export type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Deploy target type
|
|
31
|
+
*/
|
|
32
|
+
export type DeployTarget = 'dokploy' | 'none';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Services selection
|
|
36
|
+
*/
|
|
37
|
+
export interface ServicesSelection {
|
|
38
|
+
db: boolean;
|
|
39
|
+
cache: boolean;
|
|
40
|
+
mail: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
24
43
|
/**
|
|
25
44
|
* Options collected from user prompts
|
|
26
45
|
*/
|
|
@@ -35,6 +54,12 @@ export interface TemplateOptions {
|
|
|
35
54
|
monorepo: boolean;
|
|
36
55
|
/** Path for the API app in monorepo (e.g., 'apps/api') */
|
|
37
56
|
apiPath: string;
|
|
57
|
+
/** Selected package manager */
|
|
58
|
+
packageManager: PackageManager;
|
|
59
|
+
/** Deploy target */
|
|
60
|
+
deployTarget: DeployTarget;
|
|
61
|
+
/** Services selection */
|
|
62
|
+
services: ServicesSelection;
|
|
38
63
|
}
|
|
39
64
|
|
|
40
65
|
/**
|
|
@@ -57,12 +82,20 @@ export interface TemplateConfig {
|
|
|
57
82
|
files: (options: TemplateOptions) => GeneratedFile[];
|
|
58
83
|
}
|
|
59
84
|
|
|
60
|
-
export type TemplateName =
|
|
85
|
+
export type TemplateName =
|
|
86
|
+
| 'minimal'
|
|
87
|
+
| 'api'
|
|
88
|
+
| 'serverless'
|
|
89
|
+
| 'worker'
|
|
90
|
+
| 'fullstack';
|
|
61
91
|
|
|
62
92
|
/**
|
|
63
93
|
* All available templates
|
|
64
94
|
*/
|
|
65
|
-
export const templates: Record<
|
|
95
|
+
export const templates: Record<
|
|
96
|
+
Exclude<TemplateName, 'fullstack'>,
|
|
97
|
+
TemplateConfig
|
|
98
|
+
> = {
|
|
66
99
|
minimal: minimalTemplate,
|
|
67
100
|
api: apiTemplate,
|
|
68
101
|
serverless: serverlessTemplate,
|
|
@@ -70,9 +103,25 @@ export const templates: Record<TemplateName, TemplateConfig> = {
|
|
|
70
103
|
};
|
|
71
104
|
|
|
72
105
|
/**
|
|
73
|
-
* Template choices for prompts
|
|
106
|
+
* Template choices for prompts (Story 1.11 simplified to api + fullstack)
|
|
74
107
|
*/
|
|
75
108
|
export const templateChoices = [
|
|
109
|
+
{
|
|
110
|
+
title: 'API',
|
|
111
|
+
value: 'api' as TemplateName,
|
|
112
|
+
description: 'Single backend API with endpoints',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
title: 'Fullstack',
|
|
116
|
+
value: 'fullstack' as TemplateName,
|
|
117
|
+
description: 'Monorepo with API + Next.js + shared models',
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* All template choices (includes advanced options)
|
|
123
|
+
*/
|
|
124
|
+
export const allTemplateChoices = [
|
|
76
125
|
{
|
|
77
126
|
title: 'Minimal',
|
|
78
127
|
value: 'minimal' as TemplateName,
|
|
@@ -83,6 +132,11 @@ export const templateChoices = [
|
|
|
83
132
|
value: 'api' as TemplateName,
|
|
84
133
|
description: 'Full API with auth, database, services',
|
|
85
134
|
},
|
|
135
|
+
{
|
|
136
|
+
title: 'Fullstack',
|
|
137
|
+
value: 'fullstack' as TemplateName,
|
|
138
|
+
description: 'Monorepo with API + Next.js + shared models',
|
|
139
|
+
},
|
|
86
140
|
{
|
|
87
141
|
title: 'Serverless',
|
|
88
142
|
value: 'serverless' as TemplateName,
|
|
@@ -132,13 +186,87 @@ export const routesStructureChoices = [
|
|
|
132
186
|
},
|
|
133
187
|
];
|
|
134
188
|
|
|
189
|
+
/**
|
|
190
|
+
* Package manager choices for prompts
|
|
191
|
+
*/
|
|
192
|
+
export const packageManagerChoices = [
|
|
193
|
+
{
|
|
194
|
+
title: 'pnpm',
|
|
195
|
+
value: 'pnpm' as PackageManager,
|
|
196
|
+
description: 'Fast, disk space efficient (recommended)',
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
title: 'npm',
|
|
200
|
+
value: 'npm' as PackageManager,
|
|
201
|
+
description: 'Node.js default package manager',
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
title: 'yarn',
|
|
205
|
+
value: 'yarn' as PackageManager,
|
|
206
|
+
description: 'Yarn package manager',
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
title: 'bun',
|
|
210
|
+
value: 'bun' as PackageManager,
|
|
211
|
+
description: 'Fast JavaScript runtime and package manager',
|
|
212
|
+
},
|
|
213
|
+
];
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Deploy target choices for prompts
|
|
217
|
+
*/
|
|
218
|
+
export const deployTargetChoices = [
|
|
219
|
+
{
|
|
220
|
+
title: 'Dokploy',
|
|
221
|
+
value: 'dokploy' as DeployTarget,
|
|
222
|
+
description: 'Deploy to Dokploy (Docker-based hosting)',
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
title: 'Configure later',
|
|
226
|
+
value: 'none' as DeployTarget,
|
|
227
|
+
description: 'Skip deployment setup for now',
|
|
228
|
+
},
|
|
229
|
+
];
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Services choices for multi-select prompt
|
|
233
|
+
*/
|
|
234
|
+
export const servicesChoices = [
|
|
235
|
+
{
|
|
236
|
+
title: 'PostgreSQL',
|
|
237
|
+
value: 'db',
|
|
238
|
+
description: 'PostgreSQL database',
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
title: 'Redis',
|
|
242
|
+
value: 'cache',
|
|
243
|
+
description: 'Redis cache',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
title: 'Mailpit',
|
|
247
|
+
value: 'mail',
|
|
248
|
+
description: 'Email testing service (dev only)',
|
|
249
|
+
},
|
|
250
|
+
];
|
|
251
|
+
|
|
135
252
|
/**
|
|
136
253
|
* Get a template by name
|
|
137
254
|
*/
|
|
138
|
-
export function getTemplate(name: TemplateName): TemplateConfig {
|
|
255
|
+
export function getTemplate(name: TemplateName): TemplateConfig | null {
|
|
256
|
+
if (name === 'fullstack') {
|
|
257
|
+
// Fullstack template is handled specially, uses api template as base
|
|
258
|
+
return templates.api;
|
|
259
|
+
}
|
|
139
260
|
const template = templates[name];
|
|
140
261
|
if (!template) {
|
|
141
262
|
throw new Error(`Unknown template: ${name}`);
|
|
142
263
|
}
|
|
143
264
|
return template;
|
|
144
265
|
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Check if a template is the fullstack monorepo template
|
|
269
|
+
*/
|
|
270
|
+
export function isFullstackTemplate(name: TemplateName): boolean {
|
|
271
|
+
return name === 'fullstack';
|
|
272
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { GEEKMIDAS_VERSIONS } from '../versions.js';
|
|
1
2
|
import type {
|
|
2
3
|
GeneratedFile,
|
|
3
4
|
TemplateConfig,
|
|
@@ -9,16 +10,16 @@ export const minimalTemplate: TemplateConfig = {
|
|
|
9
10
|
description: 'Basic health endpoint',
|
|
10
11
|
|
|
11
12
|
dependencies: {
|
|
12
|
-
'@geekmidas/constructs': '
|
|
13
|
-
'@geekmidas/envkit': '
|
|
14
|
-
'@geekmidas/logger': '
|
|
13
|
+
'@geekmidas/constructs': GEEKMIDAS_VERSIONS['@geekmidas/constructs'],
|
|
14
|
+
'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],
|
|
15
|
+
'@geekmidas/logger': GEEKMIDAS_VERSIONS['@geekmidas/logger'],
|
|
15
16
|
hono: '~4.8.2',
|
|
16
17
|
pino: '~9.6.0',
|
|
17
18
|
},
|
|
18
19
|
|
|
19
20
|
devDependencies: {
|
|
20
|
-
'@biomejs/biome': '~
|
|
21
|
-
'@geekmidas/cli': '
|
|
21
|
+
'@biomejs/biome': '~2.3.0',
|
|
22
|
+
'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],
|
|
22
23
|
'@types/node': '~22.0.0',
|
|
23
24
|
tsx: '~4.20.0',
|
|
24
25
|
turbo: '~2.3.0',
|
|
@@ -61,14 +62,17 @@ export const logger = createLogger();
|
|
|
61
62
|
// src/config/env.ts
|
|
62
63
|
{
|
|
63
64
|
path: 'src/config/env.ts',
|
|
64
|
-
content: `import {
|
|
65
|
+
content: `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
66
|
+
import { EnvironmentParser } from '@geekmidas/envkit';
|
|
65
67
|
|
|
66
|
-
export const envParser = new EnvironmentParser(process.env);
|
|
68
|
+
export const envParser = new EnvironmentParser({ ...process.env, ...Credentials });
|
|
67
69
|
|
|
70
|
+
// Global config - only minimal shared values
|
|
71
|
+
// Service-specific config should be parsed in each service
|
|
68
72
|
export const config = envParser
|
|
69
73
|
.create((get) => ({
|
|
70
|
-
|
|
71
|
-
|
|
74
|
+
nodeEnv: get('NODE_ENV').enum(['development', 'test', 'production']).default('development'),
|
|
75
|
+
stage: get('STAGE').enum(['development', 'staging', 'production']).default('development'),
|
|
72
76
|
}))
|
|
73
77
|
.parse();
|
|
74
78
|
`,
|
|
@@ -85,7 +89,7 @@ export const config = envParser
|
|
|
85
89
|
path: getRoutePath('health.ts'),
|
|
86
90
|
content: `import { e } from '@geekmidas/constructs/endpoints';
|
|
87
91
|
|
|
88
|
-
export
|
|
92
|
+
export const healthEndpoint = e
|
|
89
93
|
.get('/health')
|
|
90
94
|
.handle(async () => ({
|
|
91
95
|
status: 'ok',
|
|
@@ -97,28 +101,9 @@ export default e
|
|
|
97
101
|
|
|
98
102
|
// Add database service if enabled
|
|
99
103
|
if (options.database) {
|
|
100
|
-
// Update env.ts to include database config
|
|
101
|
-
files[0] = {
|
|
102
|
-
path: 'src/config/env.ts',
|
|
103
|
-
content: `import { EnvironmentParser } from '@geekmidas/envkit';
|
|
104
|
-
|
|
105
|
-
export const envParser = new EnvironmentParser(process.env);
|
|
106
|
-
|
|
107
|
-
export const config = envParser
|
|
108
|
-
.create((get) => ({
|
|
109
|
-
port: get('PORT').string().transform(Number).default(3000),
|
|
110
|
-
nodeEnv: get('NODE_ENV').string().default('development'),
|
|
111
|
-
database: {
|
|
112
|
-
url: get('DATABASE_URL').string().default('postgresql://localhost:5432/mydb'),
|
|
113
|
-
},
|
|
114
|
-
}))
|
|
115
|
-
.parse();
|
|
116
|
-
`,
|
|
117
|
-
};
|
|
118
|
-
|
|
119
104
|
files.push({
|
|
120
105
|
path: 'src/services/database.ts',
|
|
121
|
-
content: `import type { Service } from '@geekmidas/services';
|
|
106
|
+
content: `import type { Service, ServiceRegisterOptions } from '@geekmidas/services';
|
|
122
107
|
import { Kysely, PostgresDialect } from 'kysely';
|
|
123
108
|
import pg from 'pg';
|
|
124
109
|
|
|
@@ -129,18 +114,24 @@ export interface Database {
|
|
|
129
114
|
|
|
130
115
|
export const databaseService = {
|
|
131
116
|
serviceName: 'database' as const,
|
|
132
|
-
async register(envParser) {
|
|
117
|
+
async register({ envParser, context }: ServiceRegisterOptions) {
|
|
118
|
+
const logger = context.getLogger();
|
|
119
|
+
logger.info('Connecting to database');
|
|
120
|
+
|
|
133
121
|
const config = envParser
|
|
134
122
|
.create((get) => ({
|
|
135
123
|
url: get('DATABASE_URL').string(),
|
|
136
124
|
}))
|
|
137
125
|
.parse();
|
|
138
126
|
|
|
139
|
-
|
|
127
|
+
const db = new Kysely<Database>({
|
|
140
128
|
dialect: new PostgresDialect({
|
|
141
129
|
pool: new pg.Pool({ connectionString: config.url }),
|
|
142
130
|
}),
|
|
143
131
|
});
|
|
132
|
+
|
|
133
|
+
logger.info('Database connection established');
|
|
134
|
+
return db;
|
|
144
135
|
},
|
|
145
136
|
} satisfies Service<'database', Kysely<Database>>;
|
|
146
137
|
`,
|
|
@@ -169,13 +160,20 @@ export const telescope = new Telescope({
|
|
|
169
160
|
content: `import { Direction, InMemoryMonitoringStorage, Studio } from '@geekmidas/studio';
|
|
170
161
|
import { Kysely, PostgresDialect } from 'kysely';
|
|
171
162
|
import pg from 'pg';
|
|
172
|
-
import type { Database } from '../services/database';
|
|
173
|
-
import {
|
|
163
|
+
import type { Database } from '../services/database.js';
|
|
164
|
+
import { envParser } from './env.js';
|
|
165
|
+
|
|
166
|
+
// Parse database config for Studio
|
|
167
|
+
const studioConfig = envParser
|
|
168
|
+
.create((get) => ({
|
|
169
|
+
databaseUrl: get('DATABASE_URL').string(),
|
|
170
|
+
}))
|
|
171
|
+
.parse();
|
|
174
172
|
|
|
175
173
|
// Create a Kysely instance for Studio
|
|
176
174
|
const db = new Kysely<Database>({
|
|
177
175
|
dialect: new PostgresDialect({
|
|
178
|
-
pool: new pg.Pool({ connectionString:
|
|
176
|
+
pool: new pg.Pool({ connectionString: studioConfig.databaseUrl }),
|
|
179
177
|
}),
|
|
180
178
|
});
|
|
181
179
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { GEEKMIDAS_VERSIONS } from '../versions.js';
|
|
1
2
|
import type {
|
|
2
3
|
GeneratedFile,
|
|
3
4
|
TemplateConfig,
|
|
@@ -9,17 +10,17 @@ export const serverlessTemplate: TemplateConfig = {
|
|
|
9
10
|
description: 'AWS Lambda handlers',
|
|
10
11
|
|
|
11
12
|
dependencies: {
|
|
12
|
-
'@geekmidas/constructs': '
|
|
13
|
-
'@geekmidas/envkit': '
|
|
14
|
-
'@geekmidas/logger': '
|
|
15
|
-
'@geekmidas/cloud': '
|
|
13
|
+
'@geekmidas/constructs': GEEKMIDAS_VERSIONS['@geekmidas/constructs'],
|
|
14
|
+
'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],
|
|
15
|
+
'@geekmidas/logger': GEEKMIDAS_VERSIONS['@geekmidas/logger'],
|
|
16
|
+
'@geekmidas/cloud': GEEKMIDAS_VERSIONS['@geekmidas/cloud'],
|
|
16
17
|
hono: '~4.8.2',
|
|
17
18
|
pino: '~9.6.0',
|
|
18
19
|
},
|
|
19
20
|
|
|
20
21
|
devDependencies: {
|
|
21
|
-
'@biomejs/biome': '~
|
|
22
|
-
'@geekmidas/cli': '
|
|
22
|
+
'@biomejs/biome': '~2.3.0',
|
|
23
|
+
'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],
|
|
23
24
|
'@types/aws-lambda': '~8.10.92',
|
|
24
25
|
'@types/node': '~22.0.0',
|
|
25
26
|
tsx: '~4.20.0',
|
|
@@ -63,21 +64,17 @@ export const logger = createLogger();
|
|
|
63
64
|
// src/config/env.ts
|
|
64
65
|
{
|
|
65
66
|
path: 'src/config/env.ts',
|
|
66
|
-
content: `import {
|
|
67
|
+
content: `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
68
|
+
import { EnvironmentParser } from '@geekmidas/envkit';
|
|
67
69
|
|
|
68
|
-
export const envParser = new EnvironmentParser(process.env);
|
|
70
|
+
export const envParser = new EnvironmentParser({ ...process.env, ...Credentials });
|
|
69
71
|
|
|
72
|
+
// Global config - only minimal shared values
|
|
73
|
+
// Service-specific config should be parsed in each service
|
|
70
74
|
export const config = envParser
|
|
71
75
|
.create((get) => ({
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
options.database
|
|
75
|
-
? `
|
|
76
|
-
database: {
|
|
77
|
-
url: get('DATABASE_URL').string(),
|
|
78
|
-
},`
|
|
79
|
-
: ''
|
|
80
|
-
}
|
|
76
|
+
nodeEnv: get('NODE_ENV').enum(['development', 'test', 'production']).default('development'),
|
|
77
|
+
stage: get('STAGE').enum(['dev', 'staging', 'prod']).default('dev'),
|
|
81
78
|
}))
|
|
82
79
|
.parse();
|
|
83
80
|
`,
|
|
@@ -94,7 +91,7 @@ export const config = envParser
|
|
|
94
91
|
path: getRoutePath('health.ts'),
|
|
95
92
|
content: `import { e } from '@geekmidas/constructs/endpoints';
|
|
96
93
|
|
|
97
|
-
export
|
|
94
|
+
export const healthEndpoint = e
|
|
98
95
|
.get('/health')
|
|
99
96
|
.handle(async () => ({
|
|
100
97
|
status: 'ok',
|
|
@@ -110,7 +107,7 @@ export default e
|
|
|
110
107
|
content: `import { f } from '@geekmidas/constructs/functions';
|
|
111
108
|
import { z } from 'zod';
|
|
112
109
|
|
|
113
|
-
export
|
|
110
|
+
export const helloFunction = f
|
|
114
111
|
.input(z.object({ name: z.string() }))
|
|
115
112
|
.output(z.object({ message: z.string() }))
|
|
116
113
|
.handle(async ({ input }) => ({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { GEEKMIDAS_VERSIONS } from '../versions.js';
|
|
1
2
|
import type {
|
|
2
3
|
GeneratedFile,
|
|
3
4
|
TemplateConfig,
|
|
@@ -9,17 +10,17 @@ export const workerTemplate: TemplateConfig = {
|
|
|
9
10
|
description: 'Background job processing',
|
|
10
11
|
|
|
11
12
|
dependencies: {
|
|
12
|
-
'@geekmidas/constructs': '
|
|
13
|
-
'@geekmidas/envkit': '
|
|
14
|
-
'@geekmidas/logger': '
|
|
15
|
-
'@geekmidas/events': '
|
|
13
|
+
'@geekmidas/constructs': GEEKMIDAS_VERSIONS['@geekmidas/constructs'],
|
|
14
|
+
'@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],
|
|
15
|
+
'@geekmidas/logger': GEEKMIDAS_VERSIONS['@geekmidas/logger'],
|
|
16
|
+
'@geekmidas/events': GEEKMIDAS_VERSIONS['@geekmidas/events'],
|
|
16
17
|
hono: '~4.8.2',
|
|
17
18
|
pino: '~9.6.0',
|
|
18
19
|
},
|
|
19
20
|
|
|
20
21
|
devDependencies: {
|
|
21
|
-
'@biomejs/biome': '~
|
|
22
|
-
'@geekmidas/cli': '
|
|
22
|
+
'@biomejs/biome': '~2.3.0',
|
|
23
|
+
'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],
|
|
23
24
|
'@types/node': '~22.0.0',
|
|
24
25
|
tsx: '~4.20.0',
|
|
25
26
|
turbo: '~2.3.0',
|
|
@@ -62,24 +63,17 @@ export const logger = createLogger();
|
|
|
62
63
|
// src/config/env.ts
|
|
63
64
|
{
|
|
64
65
|
path: 'src/config/env.ts',
|
|
65
|
-
content: `import {
|
|
66
|
+
content: `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
67
|
+
import { EnvironmentParser } from '@geekmidas/envkit';
|
|
66
68
|
|
|
67
|
-
export const envParser = new EnvironmentParser(process.env);
|
|
69
|
+
export const envParser = new EnvironmentParser({ ...process.env, ...Credentials });
|
|
68
70
|
|
|
71
|
+
// Global config - only minimal shared values
|
|
72
|
+
// Service-specific config should be parsed in each service
|
|
69
73
|
export const config = envParser
|
|
70
74
|
.create((get) => ({
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
rabbitmq: {
|
|
74
|
-
url: get('RABBITMQ_URL').string().default('amqp://localhost:5672'),
|
|
75
|
-
},${
|
|
76
|
-
options.database
|
|
77
|
-
? `
|
|
78
|
-
database: {
|
|
79
|
-
url: get('DATABASE_URL').string().default('postgresql://localhost:5432/mydb'),
|
|
80
|
-
},`
|
|
81
|
-
: ''
|
|
82
|
-
}
|
|
75
|
+
nodeEnv: get('NODE_ENV').enum(['development', 'test', 'production']).default('development'),
|
|
76
|
+
stage: get('STAGE').enum(['development', 'staging', 'production']).default('development'),
|
|
83
77
|
}))
|
|
84
78
|
.parse();
|
|
85
79
|
`,
|
|
@@ -96,7 +90,7 @@ export const config = envParser
|
|
|
96
90
|
path: getRoutePath('health.ts'),
|
|
97
91
|
content: `import { e } from '@geekmidas/constructs/endpoints';
|
|
98
92
|
|
|
99
|
-
export
|
|
93
|
+
export const healthEndpoint = e
|
|
100
94
|
.get('/health')
|
|
101
95
|
.handle(async () => ({
|
|
102
96
|
status: 'ok',
|
|
@@ -118,14 +112,45 @@ export type AppEvents =
|
|
|
118
112
|
`,
|
|
119
113
|
},
|
|
120
114
|
|
|
115
|
+
// src/events/publisher.ts
|
|
116
|
+
{
|
|
117
|
+
path: 'src/events/publisher.ts',
|
|
118
|
+
content: `import type { Service, ServiceRegisterOptions } from '@geekmidas/services';
|
|
119
|
+
import { Publisher, type EventPublisher } from '@geekmidas/events';
|
|
120
|
+
import type { AppEvents } from './types.js';
|
|
121
|
+
|
|
122
|
+
export const eventsPublisherService = {
|
|
123
|
+
serviceName: 'events' as const,
|
|
124
|
+
async register({ envParser, context }: ServiceRegisterOptions) {
|
|
125
|
+
const logger = context.getLogger();
|
|
126
|
+
logger.info('Connecting to message broker');
|
|
127
|
+
|
|
128
|
+
const config = envParser
|
|
129
|
+
.create((get) => ({
|
|
130
|
+
url: get('RABBITMQ_URL').string().default('amqp://localhost:5672'),
|
|
131
|
+
}))
|
|
132
|
+
.parse();
|
|
133
|
+
|
|
134
|
+
const publisher = await Publisher.fromConnectionString<AppEvents>(
|
|
135
|
+
\`rabbitmq://\${config.url.replace('amqp://', '')}?exchange=events\`
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
logger.info('Message broker connection established');
|
|
139
|
+
return publisher;
|
|
140
|
+
},
|
|
141
|
+
} satisfies Service<'events', EventPublisher<AppEvents>>;
|
|
142
|
+
`,
|
|
143
|
+
},
|
|
144
|
+
|
|
121
145
|
// src/subscribers/user-events.ts
|
|
122
146
|
{
|
|
123
147
|
path: 'src/subscribers/user-events.ts',
|
|
124
148
|
content: `import { s } from '@geekmidas/constructs/subscribers';
|
|
125
|
-
import
|
|
149
|
+
import { eventsPublisherService } from '../events/publisher.js';
|
|
126
150
|
|
|
127
|
-
export
|
|
128
|
-
.
|
|
151
|
+
export const userEventsSubscriber = s
|
|
152
|
+
.publisher(eventsPublisherService)
|
|
153
|
+
.subscribe(['user.created', 'user.updated'])
|
|
129
154
|
.handle(async ({ event, logger }) => {
|
|
130
155
|
logger.info({ type: event.type, payload: event.payload }, 'Processing user event');
|
|
131
156
|
|
|
@@ -149,7 +174,7 @@ export default s<AppEvents>()
|
|
|
149
174
|
content: `import { cron } from '@geekmidas/constructs/crons';
|
|
150
175
|
|
|
151
176
|
// Run every day at midnight
|
|
152
|
-
export
|
|
177
|
+
export const cleanupCron = cron('0 0 * * *')
|
|
153
178
|
.handle(async ({ logger }) => {
|
|
154
179
|
logger.info('Running cleanup job');
|
|
155
180
|
|