@geekmidas/cli 0.6.2 → 0.8.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/config.d.cts +1 -1
- package/dist/config.d.mts +1 -1
- package/dist/index.cjs +2583 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +2578 -29
- package/dist/index.mjs.map +1 -1
- package/dist/openapi--vOy9mo4.mjs +978 -0
- package/dist/openapi--vOy9mo4.mjs.map +1 -0
- package/dist/openapi-CHhTPief.cjs +1014 -0
- package/dist/openapi-CHhTPief.cjs.map +1 -0
- package/dist/{openapi-react-query-_-B3s8v_.mjs → openapi-react-query-CcciaVu5.mjs} +1 -1
- package/dist/{openapi-react-query-_-B3s8v_.mjs.map → openapi-react-query-CcciaVu5.mjs.map} +1 -1
- package/dist/{openapi-react-query-Cp-w8_05.cjs → openapi-react-query-o5iMi8tz.cjs} +1 -1
- package/dist/{openapi-react-query-Cp-w8_05.cjs.map → openapi-react-query-o5iMi8tz.cjs.map} +1 -1
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.cjs +1 -4
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +1 -4
- package/dist/{types-KmjzMgu8.d.cts → types-DXgiA1sF.d.mts} +58 -53
- package/dist/{types-Bi7VzDUZ.d.mts → types-b-vwGpqc.d.cts} +58 -53
- package/package.json +6 -6
- package/src/__tests__/EndpointGenerator.hooks.spec.ts +204 -0
- package/src/__tests__/normalizeHooksConfig.spec.ts +63 -0
- package/src/build/index.ts +8 -1
- package/src/build/types.ts +19 -0
- package/src/dev/index.ts +153 -37
- package/src/generators/EndpointGenerator.ts +72 -5
- package/src/generators/__tests__/EndpointGenerator.spec.ts +1 -1
- package/src/init/generators/config.ts +6 -1
- package/src/init/generators/package.ts +5 -1
- package/src/init/index.ts +9 -1
- package/src/init/templates/api.ts +31 -0
- package/src/init/templates/index.ts +1 -0
- package/src/init/templates/minimal.ts +83 -0
- package/src/types.ts +57 -0
- package/tsdown.config.ts +6 -0
- package/dist/CronGenerator-CCRYptuT.mjs +0 -55
- package/dist/CronGenerator-CCRYptuT.mjs.map +0 -1
- package/dist/CronGenerator-D4TWXQbh.cjs +0 -61
- package/dist/CronGenerator-D4TWXQbh.cjs.map +0 -1
- package/dist/CronGenerator-DWS3CCZt.d.cts +0 -14
- package/dist/CronGenerator-DZjdkEjI.d.mts +0 -14
- package/dist/EndpointGenerator-DGivkPLT.mjs +0 -335
- package/dist/EndpointGenerator-DGivkPLT.mjs.map +0 -1
- package/dist/EndpointGenerator-Dh7kMtuL.d.mts +0 -19
- package/dist/EndpointGenerator-npWEDoK2.cjs +0 -341
- package/dist/EndpointGenerator-npWEDoK2.cjs.map +0 -1
- package/dist/EndpointGenerator-zBsie_7s.d.cts +0 -19
- package/dist/FunctionGenerator-BmDHo27U.d.mts +0 -14
- package/dist/FunctionGenerator-CVk0h8tO.mjs +0 -54
- package/dist/FunctionGenerator-CVk0h8tO.mjs.map +0 -1
- package/dist/FunctionGenerator-DXjXBxUd.d.cts +0 -14
- package/dist/FunctionGenerator-DYTnyr4c.cjs +0 -60
- package/dist/FunctionGenerator-DYTnyr4c.cjs.map +0 -1
- package/dist/Generator-BGY-2dgI.d.cts +0 -27
- package/dist/Generator-CDt4pB3W.mjs +0 -41
- package/dist/Generator-CDt4pB3W.mjs.map +0 -1
- package/dist/Generator-CLVplqm2.cjs +0 -47
- package/dist/Generator-CLVplqm2.cjs.map +0 -1
- package/dist/Generator-yi9DH5TN.d.mts +0 -27
- package/dist/OpenApiTsGenerator-BVS4pOH7.mjs +0 -495
- package/dist/OpenApiTsGenerator-BVS4pOH7.mjs.map +0 -1
- package/dist/OpenApiTsGenerator-gPIIyppX.cjs +0 -501
- package/dist/OpenApiTsGenerator-gPIIyppX.cjs.map +0 -1
- package/dist/SubscriberGenerator-Bb-z3Kvx.d.cts +0 -15
- package/dist/SubscriberGenerator-CwsXqCpS.d.mts +0 -15
- package/dist/SubscriberGenerator-DABaJXML.mjs +0 -200
- package/dist/SubscriberGenerator-DABaJXML.mjs.map +0 -1
- package/dist/SubscriberGenerator-D_zpNGFr.cjs +0 -206
- package/dist/SubscriberGenerator-D_zpNGFr.cjs.map +0 -1
- package/dist/api-Bp5TIl1R.mjs +0 -167
- package/dist/api-Bp5TIl1R.mjs.map +0 -1
- package/dist/api-D4W9-tdZ.cjs +0 -173
- package/dist/api-D4W9-tdZ.cjs.map +0 -1
- package/dist/build/index.cjs +0 -15
- package/dist/build/index.d.cts +0 -7
- package/dist/build/index.d.mts +0 -7
- package/dist/build/index.mjs +0 -15
- package/dist/build/manifests.cjs +0 -4
- package/dist/build/manifests.d.cts +0 -13
- package/dist/build/manifests.d.mts +0 -13
- package/dist/build/manifests.mjs +0 -3
- package/dist/build/providerResolver.cjs +0 -5
- package/dist/build/providerResolver.d.cts +0 -23
- package/dist/build/providerResolver.d.mts +0 -23
- package/dist/build/providerResolver.mjs +0 -3
- package/dist/build/types.cjs +0 -0
- package/dist/build/types.d.cts +0 -3
- package/dist/build/types.d.mts +0 -3
- package/dist/build/types.mjs +0 -0
- package/dist/build-Cu6Mi0Lf.mjs +0 -87
- package/dist/build-Cu6Mi0Lf.mjs.map +0 -1
- package/dist/build-wmt8ZcmA.cjs +0 -93
- package/dist/build-wmt8ZcmA.cjs.map +0 -1
- package/dist/config-BP1IZynR.cjs +0 -168
- package/dist/config-BP1IZynR.cjs.map +0 -1
- package/dist/config-CIzRhm_D.d.mts +0 -11
- package/dist/config-CvehIYsb.d.cts +0 -11
- package/dist/config-UCK12Lrr.mjs +0 -162
- package/dist/config-UCK12Lrr.mjs.map +0 -1
- package/dist/dev/index.cjs +0 -17
- package/dist/dev/index.d.cts +0 -36
- package/dist/dev/index.d.mts +0 -36
- package/dist/dev/index.mjs +0 -13
- package/dist/dev-BBPWSllq.mjs +0 -348
- package/dist/dev-BBPWSllq.mjs.map +0 -1
- package/dist/dev-C2lCgE53.cjs +0 -378
- package/dist/dev-C2lCgE53.cjs.map +0 -1
- package/dist/docker-2-ipZDOJ.cjs +0 -119
- package/dist/docker-2-ipZDOJ.cjs.map +0 -1
- package/dist/docker-31GNwU3F.mjs +0 -113
- package/dist/docker-31GNwU3F.mjs.map +0 -1
- package/dist/env-CQ3hXAAW.d.mts +0 -11
- package/dist/env-CS0jvg7k.cjs +0 -144
- package/dist/env-CS0jvg7k.cjs.map +0 -1
- package/dist/env-D4YFgMqo.d.cts +0 -11
- package/dist/env-DEeVOvVu.mjs +0 -138
- package/dist/env-DEeVOvVu.mjs.map +0 -1
- package/dist/generators/CronGenerator.cjs +0 -4
- package/dist/generators/CronGenerator.d.cts +0 -5
- package/dist/generators/CronGenerator.d.mts +0 -5
- package/dist/generators/CronGenerator.mjs +0 -4
- package/dist/generators/EndpointGenerator.cjs +0 -4
- package/dist/generators/EndpointGenerator.d.cts +0 -5
- package/dist/generators/EndpointGenerator.d.mts +0 -5
- package/dist/generators/EndpointGenerator.mjs +0 -4
- package/dist/generators/FunctionGenerator.cjs +0 -4
- package/dist/generators/FunctionGenerator.d.cts +0 -5
- package/dist/generators/FunctionGenerator.d.mts +0 -5
- package/dist/generators/FunctionGenerator.mjs +0 -4
- package/dist/generators/Generator.cjs +0 -3
- package/dist/generators/Generator.d.cts +0 -4
- package/dist/generators/Generator.d.mts +0 -4
- package/dist/generators/Generator.mjs +0 -3
- package/dist/generators/OpenApiTsGenerator.cjs +0 -3
- package/dist/generators/OpenApiTsGenerator.d.cts +0 -44
- package/dist/generators/OpenApiTsGenerator.d.mts +0 -44
- package/dist/generators/OpenApiTsGenerator.mjs +0 -3
- package/dist/generators/SubscriberGenerator.cjs +0 -4
- package/dist/generators/SubscriberGenerator.d.cts +0 -5
- package/dist/generators/SubscriberGenerator.d.mts +0 -5
- package/dist/generators/SubscriberGenerator.mjs +0 -4
- package/dist/generators/index.cjs +0 -12
- package/dist/generators/index.d.cts +0 -8
- package/dist/generators/index.d.mts +0 -8
- package/dist/generators/index.mjs +0 -8
- package/dist/generators-3IemvCLk.cjs +0 -0
- package/dist/generators-FNpdfN6J.mjs +0 -0
- package/dist/index-DG6xNQMH.d.cts +0 -81
- package/dist/index-DZgrOOOW.d.mts +0 -81
- package/dist/init/generators/config.cjs +0 -3
- package/dist/init/generators/config.d.cts +0 -3
- package/dist/init/generators/config.d.mts +0 -3
- package/dist/init/generators/config.mjs +0 -3
- package/dist/init/generators/docker.cjs +0 -3
- package/dist/init/generators/docker.d.cts +0 -11
- package/dist/init/generators/docker.d.mts +0 -11
- package/dist/init/generators/docker.mjs +0 -3
- package/dist/init/generators/env.cjs +0 -3
- package/dist/init/generators/env.d.cts +0 -3
- package/dist/init/generators/env.d.mts +0 -3
- package/dist/init/generators/env.mjs +0 -3
- package/dist/init/generators/index.cjs +0 -14
- package/dist/init/generators/index.d.cts +0 -6
- package/dist/init/generators/index.d.mts +0 -6
- package/dist/init/generators/index.mjs +0 -11
- package/dist/init/generators/models.cjs +0 -3
- package/dist/init/generators/models.d.cts +0 -11
- package/dist/init/generators/models.d.mts +0 -11
- package/dist/init/generators/models.mjs +0 -3
- package/dist/init/generators/monorepo.cjs +0 -3
- package/dist/init/generators/monorepo.d.cts +0 -11
- package/dist/init/generators/monorepo.d.mts +0 -11
- package/dist/init/generators/monorepo.mjs +0 -3
- package/dist/init/generators/package.cjs +0 -8
- package/dist/init/generators/package.d.cts +0 -3
- package/dist/init/generators/package.d.mts +0 -3
- package/dist/init/generators/package.mjs +0 -8
- package/dist/init/generators/source.cjs +0 -3
- package/dist/init/generators/source.d.cts +0 -3
- package/dist/init/generators/source.d.mts +0 -3
- package/dist/init/generators/source.mjs +0 -3
- package/dist/init/index.cjs +0 -16
- package/dist/init/index.d.cts +0 -17
- package/dist/init/index.d.mts +0 -17
- package/dist/init/index.mjs +0 -16
- package/dist/init/templates/api.cjs +0 -3
- package/dist/init/templates/api.d.cts +0 -7
- package/dist/init/templates/api.d.mts +0 -7
- package/dist/init/templates/api.mjs +0 -3
- package/dist/init/templates/index.cjs +0 -12
- package/dist/init/templates/index.d.cts +0 -2
- package/dist/init/templates/index.d.mts +0 -2
- package/dist/init/templates/index.mjs +0 -7
- package/dist/init/templates/minimal.cjs +0 -3
- package/dist/init/templates/minimal.d.cts +0 -7
- package/dist/init/templates/minimal.d.mts +0 -7
- package/dist/init/templates/minimal.mjs +0 -3
- package/dist/init/templates/serverless.cjs +0 -3
- package/dist/init/templates/serverless.d.cts +0 -7
- package/dist/init/templates/serverless.d.mts +0 -7
- package/dist/init/templates/serverless.mjs +0 -3
- package/dist/init/templates/worker.cjs +0 -3
- package/dist/init/templates/worker.d.cts +0 -7
- package/dist/init/templates/worker.d.mts +0 -7
- package/dist/init/templates/worker.mjs +0 -3
- package/dist/init/utils.cjs +0 -7
- package/dist/init/utils.d.cts +0 -25
- package/dist/init/utils.d.mts +0 -25
- package/dist/init/utils.mjs +0 -3
- package/dist/init-BMA7xi8r.mjs +0 -161
- package/dist/init-BMA7xi8r.mjs.map +0 -1
- package/dist/init-D-7WEk-b.cjs +0 -167
- package/dist/init-D-7WEk-b.cjs.map +0 -1
- package/dist/manifests-BNKG6AXf.mjs +0 -68
- package/dist/manifests-BNKG6AXf.mjs.map +0 -1
- package/dist/manifests-D13Ej8AE.cjs +0 -80
- package/dist/manifests-D13Ej8AE.cjs.map +0 -1
- package/dist/minimal-BkyASH_C.mjs +0 -93
- package/dist/minimal-BkyASH_C.mjs.map +0 -1
- package/dist/minimal-CSFggzdH.cjs +0 -99
- package/dist/minimal-CSFggzdH.cjs.map +0 -1
- package/dist/models-BWlDfviw.mjs +0 -115
- package/dist/models-BWlDfviw.mjs.map +0 -1
- package/dist/models-BapGSoHC.cjs +0 -121
- package/dist/models-BapGSoHC.cjs.map +0 -1
- package/dist/monorepo-BBOWhkcd.mjs +0 -184
- package/dist/monorepo-BBOWhkcd.mjs.map +0 -1
- package/dist/monorepo-CFtxHeDh.cjs +0 -190
- package/dist/monorepo-CFtxHeDh.cjs.map +0 -1
- package/dist/openapi-DA9RkPJl.mjs +0 -74
- package/dist/openapi-DA9RkPJl.mjs.map +0 -1
- package/dist/openapi-DZH6RQHk.cjs +0 -98
- package/dist/openapi-DZH6RQHk.cjs.map +0 -1
- package/dist/package-6h-7QfJZ.d.cts +0 -11
- package/dist/package-BCe_KvGv.d.mts +0 -11
- package/dist/package-C3If80n1.mjs +0 -57
- package/dist/package-C3If80n1.mjs.map +0 -1
- package/dist/package-Dk8IMBOB.cjs +0 -62
- package/dist/package-Dk8IMBOB.cjs.map +0 -1
- package/dist/providerResolver-DEVKngbC.mjs +0 -96
- package/dist/providerResolver-DEVKngbC.mjs.map +0 -1
- package/dist/providerResolver-DOTbN9jo.cjs +0 -114
- package/dist/providerResolver-DOTbN9jo.cjs.map +0 -1
- package/dist/serverless-AGOS-l3G.cjs +0 -119
- package/dist/serverless-AGOS-l3G.cjs.map +0 -1
- package/dist/serverless-D5HjJByU.mjs +0 -113
- package/dist/serverless-D5HjJByU.mjs.map +0 -1
- package/dist/source-C1cyfHcF.cjs +0 -17
- package/dist/source-C1cyfHcF.cjs.map +0 -1
- package/dist/source-C3LiNUV9.d.mts +0 -11
- package/dist/source-CkQHBpwu.mjs +0 -11
- package/dist/source-CkQHBpwu.mjs.map +0 -1
- package/dist/source-Dtcjbokc.d.cts +0 -11
- package/dist/templates-C0EMmhwb.mjs +0 -88
- package/dist/templates-C0EMmhwb.mjs.map +0 -1
- package/dist/templates-CbgQ9dw0.cjs +0 -123
- package/dist/templates-CbgQ9dw0.cjs.map +0 -1
- package/dist/types-D2xYkOal.d.mts +0 -51
- package/dist/types-DA-r8HWZ.d.cts +0 -51
- package/dist/types.cjs +0 -0
- package/dist/types.d.cts +0 -2
- package/dist/types.d.mts +0 -2
- package/dist/types.mjs +0 -0
- package/dist/utils-CKEzCxc1.mjs +0 -69
- package/dist/utils-CKEzCxc1.mjs.map +0 -1
- package/dist/utils-DSdN2MTt.cjs +0 -99
- package/dist/utils-DSdN2MTt.cjs.map +0 -1
- package/dist/worker-CGhlqNH-.cjs +0 -156
- package/dist/worker-CGhlqNH-.cjs.map +0 -1
- package/dist/worker-CiP420As.mjs +0 -150
- package/dist/worker-CiP420As.mjs.map +0 -1
package/src/dev/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ChildProcess, spawn } from 'node:child_process';
|
|
1
|
+
import { type ChildProcess, execSync, spawn } from 'node:child_process';
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import { mkdir } from 'node:fs/promises';
|
|
4
4
|
import { createServer } from 'node:net';
|
|
@@ -7,7 +7,12 @@ import chokidar from 'chokidar';
|
|
|
7
7
|
import { config as dotenvConfig } from 'dotenv';
|
|
8
8
|
import fg from 'fast-glob';
|
|
9
9
|
import { resolveProviders } from '../build/providerResolver';
|
|
10
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
BuildContext,
|
|
12
|
+
NormalizedHooksConfig,
|
|
13
|
+
NormalizedStudioConfig,
|
|
14
|
+
NormalizedTelescopeConfig,
|
|
15
|
+
} from '../build/types';
|
|
11
16
|
import { loadConfig, parseModuleConfig } from '../config';
|
|
12
17
|
import {
|
|
13
18
|
CronGenerator,
|
|
@@ -15,11 +20,16 @@ import {
|
|
|
15
20
|
FunctionGenerator,
|
|
16
21
|
SubscriberGenerator,
|
|
17
22
|
} from '../generators';
|
|
18
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
OPENAPI_OUTPUT_PATH,
|
|
25
|
+
generateOpenApi,
|
|
26
|
+
resolveOpenApiConfig,
|
|
27
|
+
} from '../openapi';
|
|
19
28
|
import type {
|
|
20
29
|
GkmConfig,
|
|
21
30
|
LegacyProvider,
|
|
22
31
|
Runtime,
|
|
32
|
+
StudioConfig,
|
|
23
33
|
TelescopeConfig,
|
|
24
34
|
} from '../types';
|
|
25
35
|
|
|
@@ -153,6 +163,71 @@ export function normalizeTelescopeConfig(
|
|
|
153
163
|
};
|
|
154
164
|
}
|
|
155
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Normalize studio configuration
|
|
168
|
+
* @internal Exported for testing
|
|
169
|
+
*/
|
|
170
|
+
export function normalizeStudioConfig(
|
|
171
|
+
config: GkmConfig['studio'],
|
|
172
|
+
): NormalizedStudioConfig | undefined {
|
|
173
|
+
if (config === false) {
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Handle string path (e.g., './src/config/studio')
|
|
178
|
+
if (typeof config === 'string') {
|
|
179
|
+
const { path: studioPath, importPattern: studioImportPattern } =
|
|
180
|
+
parseModuleConfig(config, 'studio');
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
enabled: true,
|
|
184
|
+
studioPath,
|
|
185
|
+
studioImportPattern,
|
|
186
|
+
path: '/__studio',
|
|
187
|
+
schema: 'public',
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Default to enabled in development mode
|
|
192
|
+
const isEnabled =
|
|
193
|
+
config === true || config === undefined || config.enabled !== false;
|
|
194
|
+
|
|
195
|
+
if (!isEnabled) {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const studioConfig: StudioConfig = typeof config === 'object' ? config : {};
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
enabled: true,
|
|
203
|
+
path: studioConfig.path ?? '/__studio',
|
|
204
|
+
schema: studioConfig.schema ?? 'public',
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Normalize hooks configuration
|
|
210
|
+
* @internal Exported for testing
|
|
211
|
+
*/
|
|
212
|
+
export function normalizeHooksConfig(
|
|
213
|
+
config: GkmConfig['hooks'],
|
|
214
|
+
): NormalizedHooksConfig | undefined {
|
|
215
|
+
if (!config?.server) {
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Resolve the path (handle .ts extension)
|
|
220
|
+
const serverPath = config.server.endsWith('.ts')
|
|
221
|
+
? config.server
|
|
222
|
+
: `${config.server}.ts`;
|
|
223
|
+
|
|
224
|
+
const resolvedPath = resolve(process.cwd(), serverPath);
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
serverHooksPath: resolvedPath,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
156
231
|
export interface DevOptions {
|
|
157
232
|
port?: number;
|
|
158
233
|
enableOpenApi?: boolean;
|
|
@@ -207,10 +282,24 @@ export async function devCommand(options: DevOptions): Promise<void> {
|
|
|
207
282
|
logger.log(`🔭 Telescope enabled at ${telescope.path}`);
|
|
208
283
|
}
|
|
209
284
|
|
|
285
|
+
// Normalize studio configuration
|
|
286
|
+
const studio = normalizeStudioConfig(config.studio);
|
|
287
|
+
if (studio) {
|
|
288
|
+
logger.log(`🗄️ Studio enabled at ${studio.path}`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Normalize hooks configuration
|
|
292
|
+
const hooks = normalizeHooksConfig(config.hooks);
|
|
293
|
+
if (hooks) {
|
|
294
|
+
logger.log(`🪝 Server hooks enabled from ${config.hooks?.server}`);
|
|
295
|
+
}
|
|
296
|
+
|
|
210
297
|
// Resolve OpenAPI configuration
|
|
211
298
|
const openApiConfig = resolveOpenApiConfig(config);
|
|
212
|
-
if
|
|
213
|
-
|
|
299
|
+
// Enable OpenAPI docs endpoint if either root config or provider config enables it
|
|
300
|
+
const enableOpenApi = openApiConfig.enabled || resolved.enableOpenApi;
|
|
301
|
+
if (enableOpenApi) {
|
|
302
|
+
logger.log(`📄 OpenAPI output: ${OPENAPI_OUTPUT_PATH}`);
|
|
214
303
|
}
|
|
215
304
|
|
|
216
305
|
const buildContext: BuildContext = {
|
|
@@ -219,6 +308,8 @@ export async function devCommand(options: DevOptions): Promise<void> {
|
|
|
219
308
|
loggerPath,
|
|
220
309
|
loggerImportPattern,
|
|
221
310
|
telescope,
|
|
311
|
+
studio,
|
|
312
|
+
hooks,
|
|
222
313
|
};
|
|
223
314
|
|
|
224
315
|
// Build initial version
|
|
@@ -226,11 +317,11 @@ export async function devCommand(options: DevOptions): Promise<void> {
|
|
|
226
317
|
config,
|
|
227
318
|
buildContext,
|
|
228
319
|
resolved.providers[0] as LegacyProvider,
|
|
229
|
-
|
|
320
|
+
enableOpenApi,
|
|
230
321
|
);
|
|
231
322
|
|
|
232
323
|
// Generate OpenAPI spec on startup
|
|
233
|
-
if (
|
|
324
|
+
if (enableOpenApi) {
|
|
234
325
|
await generateOpenApi(config);
|
|
235
326
|
}
|
|
236
327
|
|
|
@@ -241,8 +332,9 @@ export async function devCommand(options: DevOptions): Promise<void> {
|
|
|
241
332
|
const devServer = new DevServer(
|
|
242
333
|
resolved.providers[0] as LegacyProvider,
|
|
243
334
|
options.port || 3000,
|
|
244
|
-
|
|
335
|
+
enableOpenApi,
|
|
245
336
|
telescope,
|
|
337
|
+
studio,
|
|
246
338
|
runtime,
|
|
247
339
|
);
|
|
248
340
|
|
|
@@ -252,6 +344,9 @@ export async function devCommand(options: DevOptions): Promise<void> {
|
|
|
252
344
|
const envParserFile = config.envParser.split('#')[0];
|
|
253
345
|
const loggerFile = config.logger.split('#')[0];
|
|
254
346
|
|
|
347
|
+
// Get hooks file path for watching
|
|
348
|
+
const hooksFile = config.hooks?.server?.split('#')[0];
|
|
349
|
+
|
|
255
350
|
const watchPatterns = [
|
|
256
351
|
config.routes,
|
|
257
352
|
...(config.functions ? [config.functions] : []),
|
|
@@ -260,6 +355,10 @@ export async function devCommand(options: DevOptions): Promise<void> {
|
|
|
260
355
|
// Add .ts extension if not present for config files
|
|
261
356
|
envParserFile.endsWith('.ts') ? envParserFile : `${envParserFile}.ts`,
|
|
262
357
|
loggerFile.endsWith('.ts') ? loggerFile : `${loggerFile}.ts`,
|
|
358
|
+
// Add hooks file to watch list
|
|
359
|
+
...(hooksFile
|
|
360
|
+
? [hooksFile.endsWith('.ts') ? hooksFile : `${hooksFile}.ts`]
|
|
361
|
+
: []),
|
|
263
362
|
].flat();
|
|
264
363
|
|
|
265
364
|
// Normalize patterns - remove leading ./ when using cwd option
|
|
@@ -317,11 +416,11 @@ export async function devCommand(options: DevOptions): Promise<void> {
|
|
|
317
416
|
config,
|
|
318
417
|
buildContext,
|
|
319
418
|
resolved.providers[0] as LegacyProvider,
|
|
320
|
-
|
|
419
|
+
enableOpenApi,
|
|
321
420
|
);
|
|
322
421
|
|
|
323
422
|
// Regenerate OpenAPI if enabled
|
|
324
|
-
if (
|
|
423
|
+
if (enableOpenApi) {
|
|
325
424
|
await generateOpenApi(config, { silent: true });
|
|
326
425
|
}
|
|
327
426
|
|
|
@@ -334,11 +433,21 @@ export async function devCommand(options: DevOptions): Promise<void> {
|
|
|
334
433
|
});
|
|
335
434
|
|
|
336
435
|
// Handle graceful shutdown
|
|
337
|
-
|
|
436
|
+
let isShuttingDown = false;
|
|
437
|
+
const shutdown = () => {
|
|
438
|
+
if (isShuttingDown) return;
|
|
439
|
+
isShuttingDown = true;
|
|
440
|
+
|
|
338
441
|
logger.log('\n🛑 Shutting down...');
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
442
|
+
|
|
443
|
+
// Use sync-style shutdown to ensure it completes before exit
|
|
444
|
+
Promise.all([watcher.close(), devServer.stop()])
|
|
445
|
+
.catch((err) => {
|
|
446
|
+
logger.error('Error during shutdown:', err);
|
|
447
|
+
})
|
|
448
|
+
.finally(() => {
|
|
449
|
+
process.exit(0);
|
|
450
|
+
});
|
|
342
451
|
};
|
|
343
452
|
|
|
344
453
|
process.on('SIGINT', shutdown);
|
|
@@ -392,6 +501,7 @@ class DevServer {
|
|
|
392
501
|
private requestedPort: number,
|
|
393
502
|
private enableOpenApi: boolean,
|
|
394
503
|
private telescope?: NormalizedTelescopeConfig,
|
|
504
|
+
private studio?: NormalizedStudioConfig,
|
|
395
505
|
private runtime: Runtime = 'node',
|
|
396
506
|
) {
|
|
397
507
|
this.actualPort = requestedPort;
|
|
@@ -455,7 +565,7 @@ class DevServer {
|
|
|
455
565
|
logger.log(`\n🎉 Server running at http://localhost:${this.actualPort}`);
|
|
456
566
|
if (this.enableOpenApi) {
|
|
457
567
|
logger.log(
|
|
458
|
-
`📚 API Docs available at http://localhost:${this.actualPort}/
|
|
568
|
+
`📚 API Docs available at http://localhost:${this.actualPort}/__docs`,
|
|
459
569
|
);
|
|
460
570
|
}
|
|
461
571
|
if (this.telescope) {
|
|
@@ -463,44 +573,50 @@ class DevServer {
|
|
|
463
573
|
`🔭 Telescope available at http://localhost:${this.actualPort}${this.telescope.path}`,
|
|
464
574
|
);
|
|
465
575
|
}
|
|
576
|
+
if (this.studio) {
|
|
577
|
+
logger.log(
|
|
578
|
+
`🗄️ Studio available at http://localhost:${this.actualPort}${this.studio.path}`,
|
|
579
|
+
);
|
|
580
|
+
}
|
|
466
581
|
}
|
|
467
582
|
}
|
|
468
583
|
|
|
469
584
|
async stop(): Promise<void> {
|
|
585
|
+
const port = this.actualPort;
|
|
586
|
+
|
|
470
587
|
if (this.serverProcess && this.isRunning) {
|
|
471
588
|
const pid = this.serverProcess.pid;
|
|
472
589
|
|
|
473
|
-
//
|
|
590
|
+
// Use SIGKILL directly since the server ignores SIGTERM
|
|
474
591
|
if (pid) {
|
|
475
592
|
try {
|
|
476
|
-
process.kill(-pid, '
|
|
593
|
+
process.kill(-pid, 'SIGKILL');
|
|
477
594
|
} catch {
|
|
478
|
-
|
|
595
|
+
try {
|
|
596
|
+
process.kill(pid, 'SIGKILL');
|
|
597
|
+
} catch {
|
|
598
|
+
// Process might already be dead
|
|
599
|
+
}
|
|
479
600
|
}
|
|
480
601
|
}
|
|
481
602
|
|
|
482
|
-
// Wait for process to exit
|
|
483
|
-
await new Promise<void>((resolve) => {
|
|
484
|
-
const timeout = setTimeout(() => {
|
|
485
|
-
if (pid) {
|
|
486
|
-
try {
|
|
487
|
-
process.kill(-pid, 'SIGKILL');
|
|
488
|
-
} catch {
|
|
489
|
-
// Process might already be dead
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
resolve();
|
|
493
|
-
}, 3000);
|
|
494
|
-
|
|
495
|
-
this.serverProcess?.on('exit', () => {
|
|
496
|
-
clearTimeout(timeout);
|
|
497
|
-
resolve();
|
|
498
|
-
});
|
|
499
|
-
});
|
|
500
|
-
|
|
501
603
|
this.serverProcess = null;
|
|
502
604
|
this.isRunning = false;
|
|
503
605
|
}
|
|
606
|
+
|
|
607
|
+
// Also kill any processes still holding the port
|
|
608
|
+
this.killProcessesOnPort(port);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
private killProcessesOnPort(port: number): void {
|
|
612
|
+
try {
|
|
613
|
+
// Use lsof to find PIDs on the port and kill them with -9
|
|
614
|
+
execSync(`lsof -ti tcp:${port} | xargs kill -9 2>/dev/null || true`, {
|
|
615
|
+
stdio: 'ignore',
|
|
616
|
+
});
|
|
617
|
+
} catch {
|
|
618
|
+
// Ignore errors - port may already be free
|
|
619
|
+
}
|
|
504
620
|
}
|
|
505
621
|
|
|
506
622
|
async restart(): Promise<void> {
|
|
@@ -289,12 +289,12 @@ const endpoints: Endpoint<any, any, any, any, any, any, any, any, any, any, any,
|
|
|
289
289
|
${allExportNames.join(',\n ')}
|
|
290
290
|
];
|
|
291
291
|
|
|
292
|
-
export function setupEndpoints(
|
|
292
|
+
export async function setupEndpoints(
|
|
293
293
|
app: Hono,
|
|
294
294
|
envParser: EnvironmentParser<any>,
|
|
295
295
|
logger: Logger,
|
|
296
296
|
enableOpenApi: boolean = true,
|
|
297
|
-
): void {
|
|
297
|
+
): Promise<void> {
|
|
298
298
|
const serviceDiscovery = ServiceDiscovery.getInstance(
|
|
299
299
|
logger,
|
|
300
300
|
envParser
|
|
@@ -302,7 +302,7 @@ export function setupEndpoints(
|
|
|
302
302
|
|
|
303
303
|
// Configure OpenAPI options based on enableOpenApi flag
|
|
304
304
|
const openApiOptions: any = enableOpenApi ? {
|
|
305
|
-
docsPath: '/
|
|
305
|
+
docsPath: '/__docs',
|
|
306
306
|
openApiOptions: {
|
|
307
307
|
title: 'API Documentation',
|
|
308
308
|
version: '1.0.0',
|
|
@@ -311,6 +311,16 @@ export function setupEndpoints(
|
|
|
311
311
|
} : { docsPath: false };
|
|
312
312
|
|
|
313
313
|
HonoEndpoint.addRoutes(endpoints, serviceDiscovery, app, openApiOptions);
|
|
314
|
+
|
|
315
|
+
// Add Swagger UI if OpenAPI is enabled
|
|
316
|
+
if (enableOpenApi) {
|
|
317
|
+
try {
|
|
318
|
+
const { swaggerUI } = await import('@hono/swagger-ui');
|
|
319
|
+
app.get('/__docs/ui', swaggerUI({ url: '/__docs' }));
|
|
320
|
+
} catch {
|
|
321
|
+
// @hono/swagger-ui not installed, skip Swagger UI
|
|
322
|
+
}
|
|
323
|
+
}
|
|
314
324
|
}
|
|
315
325
|
`;
|
|
316
326
|
|
|
@@ -338,6 +348,10 @@ export function setupEndpoints(
|
|
|
338
348
|
const telescopeWebSocketEnabled = context.telescope?.websocket;
|
|
339
349
|
const usesExternalTelescope = !!context.telescope?.telescopePath;
|
|
340
350
|
|
|
351
|
+
// Generate studio imports and setup if enabled
|
|
352
|
+
const studioEnabled = context.studio?.enabled;
|
|
353
|
+
const usesExternalStudio = !!context.studio?.studioPath;
|
|
354
|
+
|
|
341
355
|
// Generate imports based on whether telescope is external or inline
|
|
342
356
|
let telescopeImports = '';
|
|
343
357
|
if (telescopeEnabled) {
|
|
@@ -354,6 +368,46 @@ import { createMiddleware, createUI } from '@geekmidas/telescope/hono';`;
|
|
|
354
368
|
}
|
|
355
369
|
}
|
|
356
370
|
|
|
371
|
+
// Generate imports for studio
|
|
372
|
+
let studioImports = '';
|
|
373
|
+
if (studioEnabled) {
|
|
374
|
+
if (usesExternalStudio) {
|
|
375
|
+
const relativeStudioPath = relative(
|
|
376
|
+
dirname(appPath),
|
|
377
|
+
context.studio!.studioPath!,
|
|
378
|
+
);
|
|
379
|
+
studioImports = `import ${context.studio!.studioImportPattern} from '${relativeStudioPath}';
|
|
380
|
+
import { createStudioApp } from '@geekmidas/studio/server/hono';`;
|
|
381
|
+
} else {
|
|
382
|
+
studioImports = `// Studio requires a configured instance - use studio config path
|
|
383
|
+
// import { createStudioApp } from '@geekmidas/studio/server/hono';`;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Generate imports for server hooks
|
|
388
|
+
let hooksImports = '';
|
|
389
|
+
let beforeSetupCall = '';
|
|
390
|
+
let afterSetupCall = '';
|
|
391
|
+
if (context.hooks?.serverHooksPath) {
|
|
392
|
+
const relativeHooksPath = relative(
|
|
393
|
+
dirname(appPath),
|
|
394
|
+
context.hooks.serverHooksPath,
|
|
395
|
+
);
|
|
396
|
+
hooksImports = `import * as serverHooks from '${relativeHooksPath}';`;
|
|
397
|
+
beforeSetupCall = `
|
|
398
|
+
// Call beforeSetup hook if defined
|
|
399
|
+
if (typeof serverHooks.beforeSetup === 'function') {
|
|
400
|
+
await serverHooks.beforeSetup(honoApp, { envParser, logger });
|
|
401
|
+
}
|
|
402
|
+
`;
|
|
403
|
+
afterSetupCall = `
|
|
404
|
+
// Call afterSetup hook if defined
|
|
405
|
+
if (typeof serverHooks.afterSetup === 'function') {
|
|
406
|
+
await serverHooks.afterSetup(honoApp, { envParser, logger });
|
|
407
|
+
}
|
|
408
|
+
`;
|
|
409
|
+
}
|
|
410
|
+
|
|
357
411
|
const telescopeWebSocketSetupCode = telescopeWebSocketEnabled
|
|
358
412
|
? `
|
|
359
413
|
// Setup WebSocket for real-time telescope updates
|
|
@@ -425,6 +479,16 @@ ${telescopeWebSocketSetupCode}
|
|
|
425
479
|
}
|
|
426
480
|
}
|
|
427
481
|
|
|
482
|
+
// Generate studio setup - requires external instance
|
|
483
|
+
let studioSetup = '';
|
|
484
|
+
if (studioEnabled && usesExternalStudio) {
|
|
485
|
+
studioSetup = `
|
|
486
|
+
// Mount Studio data browser UI
|
|
487
|
+
const studioApp = createStudioApp(studio);
|
|
488
|
+
honoApp.route('${context.studio!.path}', studioApp);
|
|
489
|
+
`;
|
|
490
|
+
}
|
|
491
|
+
|
|
428
492
|
const content = `/**
|
|
429
493
|
* Generated server application
|
|
430
494
|
*
|
|
@@ -439,6 +503,8 @@ import { setupSubscribers } from './subscribers.js';
|
|
|
439
503
|
import ${context.envParserImportPattern} from '${relativeEnvParserPath}';
|
|
440
504
|
import ${context.loggerImportPattern} from '${relativeLoggerPath}';
|
|
441
505
|
${telescopeImports}
|
|
506
|
+
${studioImports}
|
|
507
|
+
${hooksImports}
|
|
442
508
|
|
|
443
509
|
export interface ServerApp {
|
|
444
510
|
app: HonoType;
|
|
@@ -484,9 +550,10 @@ export interface ServerApp {
|
|
|
484
550
|
*/
|
|
485
551
|
export async function createApp(app?: HonoType, enableOpenApi: boolean = true): Promise<ServerApp> {
|
|
486
552
|
const honoApp = app || new Hono();
|
|
487
|
-
${telescopeSetup}
|
|
553
|
+
${telescopeSetup}${beforeSetupCall}${studioSetup}
|
|
488
554
|
// Setup HTTP endpoints
|
|
489
|
-
setupEndpoints(honoApp, envParser, logger, enableOpenApi);
|
|
555
|
+
await setupEndpoints(honoApp, envParser, logger, enableOpenApi);
|
|
556
|
+
${afterSetupCall}
|
|
490
557
|
|
|
491
558
|
return {
|
|
492
559
|
app: honoApp,
|
|
@@ -111,7 +111,7 @@ describe('EndpointGenerator', () => {
|
|
|
111
111
|
// Function signature always defaults to true
|
|
112
112
|
expect(endpointsContent).toContain('enableOpenApi: boolean = true');
|
|
113
113
|
// OpenAPI options are configured based on the parameter
|
|
114
|
-
expect(endpointsContent).toContain("docsPath: '/
|
|
114
|
+
expect(endpointsContent).toContain("docsPath: '/__docs'");
|
|
115
115
|
});
|
|
116
116
|
|
|
117
117
|
itWithDir(
|
|
@@ -11,7 +11,7 @@ export function generateConfigFiles(
|
|
|
11
11
|
options: TemplateOptions,
|
|
12
12
|
template: TemplateConfig,
|
|
13
13
|
): GeneratedFile[] {
|
|
14
|
-
const { telescope, routesStructure } = options;
|
|
14
|
+
const { telescope, studio, routesStructure } = options;
|
|
15
15
|
const isServerless = template.name === 'serverless';
|
|
16
16
|
const hasWorker = template.name === 'worker';
|
|
17
17
|
|
|
@@ -54,6 +54,11 @@ export default defineConfig({
|
|
|
54
54
|
},`;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
if (studio) {
|
|
58
|
+
gkmConfig += `
|
|
59
|
+
studio: './src/config/studio#studio',`;
|
|
60
|
+
}
|
|
61
|
+
|
|
57
62
|
// Always add openapi config (output path is fixed to .gkm/openapi.ts)
|
|
58
63
|
gkmConfig += `
|
|
59
64
|
openapi: {
|
|
@@ -12,7 +12,7 @@ export function generatePackageJson(
|
|
|
12
12
|
options: TemplateOptions,
|
|
13
13
|
template: TemplateConfig,
|
|
14
14
|
): GeneratedFile[] {
|
|
15
|
-
const { name, telescope, database, monorepo } = options;
|
|
15
|
+
const { name, telescope, database, studio, monorepo } = options;
|
|
16
16
|
|
|
17
17
|
// Start with template dependencies
|
|
18
18
|
const dependencies = { ...template.dependencies };
|
|
@@ -24,6 +24,10 @@ export function generatePackageJson(
|
|
|
24
24
|
dependencies['@geekmidas/telescope'] = 'workspace:*';
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
if (studio) {
|
|
28
|
+
dependencies['@geekmidas/studio'] = 'workspace:*';
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
if (database) {
|
|
28
32
|
dependencies['@geekmidas/db'] = 'workspace:*';
|
|
29
33
|
dependencies['kysely'] = '~0.28.2';
|
package/src/init/index.ts
CHANGED
|
@@ -84,6 +84,12 @@ export async function initCommand(
|
|
|
84
84
|
message: 'Include database support (Kysely)?',
|
|
85
85
|
initial: true,
|
|
86
86
|
},
|
|
87
|
+
{
|
|
88
|
+
type: (prev) => (options.yes ? null : prev ? 'confirm' : null),
|
|
89
|
+
name: 'studio',
|
|
90
|
+
message: 'Include Studio (database browser)?',
|
|
91
|
+
initial: true,
|
|
92
|
+
},
|
|
87
93
|
{
|
|
88
94
|
type: options.yes ? null : 'select',
|
|
89
95
|
name: 'loggerType',
|
|
@@ -140,11 +146,13 @@ export async function initCommand(
|
|
|
140
146
|
|
|
141
147
|
const monorepo =
|
|
142
148
|
options.monorepo ?? (options.yes ? false : (answers.monorepo ?? false));
|
|
149
|
+
const database = options.yes ? true : (answers.database ?? true);
|
|
143
150
|
const templateOptions: TemplateOptions = {
|
|
144
151
|
name,
|
|
145
152
|
template: options.template || answers.template || 'minimal',
|
|
146
153
|
telescope: options.yes ? true : (answers.telescope ?? true),
|
|
147
|
-
database
|
|
154
|
+
database,
|
|
155
|
+
studio: database && (options.yes ? true : (answers.studio ?? true)),
|
|
148
156
|
loggerType: options.yes ? 'pino' : (answers.loggerType ?? 'pino'),
|
|
149
157
|
routesStructure: options.yes
|
|
150
158
|
? 'centralized-endpoints'
|
|
@@ -195,6 +195,37 @@ export const telescope = new Telescope({
|
|
|
195
195
|
});
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
// Add Studio config if enabled (requires database)
|
|
199
|
+
if (options.studio && options.database) {
|
|
200
|
+
files.push({
|
|
201
|
+
path: 'src/config/studio.ts',
|
|
202
|
+
content: `import { Direction, InMemoryMonitoringStorage, Studio } from '@geekmidas/studio';
|
|
203
|
+
import { Kysely, PostgresDialect } from 'kysely';
|
|
204
|
+
import pg from 'pg';
|
|
205
|
+
import type { Database } from '../services/database';
|
|
206
|
+
import { config } from './env';
|
|
207
|
+
|
|
208
|
+
// Create a Kysely instance for Studio
|
|
209
|
+
const db = new Kysely<Database>({
|
|
210
|
+
dialect: new PostgresDialect({
|
|
211
|
+
pool: new pg.Pool({ connectionString: config.database.url }),
|
|
212
|
+
}),
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
export const studio = new Studio<Database>({
|
|
216
|
+
monitoring: {
|
|
217
|
+
storage: new InMemoryMonitoringStorage({ maxEntries: 100 }),
|
|
218
|
+
},
|
|
219
|
+
data: {
|
|
220
|
+
db,
|
|
221
|
+
cursor: { field: 'id', direction: Direction.Desc },
|
|
222
|
+
},
|
|
223
|
+
enabled: process.env.NODE_ENV === 'development',
|
|
224
|
+
});
|
|
225
|
+
`,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
198
229
|
return files;
|
|
199
230
|
},
|
|
200
231
|
};
|
|
@@ -95,6 +95,58 @@ export default e
|
|
|
95
95
|
},
|
|
96
96
|
];
|
|
97
97
|
|
|
98
|
+
// Add database service if enabled
|
|
99
|
+
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
|
+
files.push({
|
|
120
|
+
path: 'src/services/database.ts',
|
|
121
|
+
content: `import type { Service } from '@geekmidas/services';
|
|
122
|
+
import { Kysely, PostgresDialect } from 'kysely';
|
|
123
|
+
import pg from 'pg';
|
|
124
|
+
|
|
125
|
+
// Define your database schema
|
|
126
|
+
export interface Database {
|
|
127
|
+
// Add your tables here
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export const databaseService = {
|
|
131
|
+
serviceName: 'database' as const,
|
|
132
|
+
async register(envParser) {
|
|
133
|
+
const config = envParser
|
|
134
|
+
.create((get) => ({
|
|
135
|
+
url: get('DATABASE_URL').string(),
|
|
136
|
+
}))
|
|
137
|
+
.parse();
|
|
138
|
+
|
|
139
|
+
return new Kysely<Database>({
|
|
140
|
+
dialect: new PostgresDialect({
|
|
141
|
+
pool: new pg.Pool({ connectionString: config.url }),
|
|
142
|
+
}),
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
} satisfies Service<'database', Kysely<Database>>;
|
|
146
|
+
`,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
98
150
|
// Add Telescope config if enabled
|
|
99
151
|
if (options.telescope) {
|
|
100
152
|
files.push({
|
|
@@ -110,6 +162,37 @@ export const telescope = new Telescope({
|
|
|
110
162
|
});
|
|
111
163
|
}
|
|
112
164
|
|
|
165
|
+
// Add Studio config if enabled (requires database)
|
|
166
|
+
if (options.studio && options.database) {
|
|
167
|
+
files.push({
|
|
168
|
+
path: 'src/config/studio.ts',
|
|
169
|
+
content: `import { Direction, InMemoryMonitoringStorage, Studio } from '@geekmidas/studio';
|
|
170
|
+
import { Kysely, PostgresDialect } from 'kysely';
|
|
171
|
+
import pg from 'pg';
|
|
172
|
+
import type { Database } from '../services/database';
|
|
173
|
+
import { config } from './env';
|
|
174
|
+
|
|
175
|
+
// Create a Kysely instance for Studio
|
|
176
|
+
const db = new Kysely<Database>({
|
|
177
|
+
dialect: new PostgresDialect({
|
|
178
|
+
pool: new pg.Pool({ connectionString: config.database.url }),
|
|
179
|
+
}),
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
export const studio = new Studio<Database>({
|
|
183
|
+
monitoring: {
|
|
184
|
+
storage: new InMemoryMonitoringStorage({ maxEntries: 100 }),
|
|
185
|
+
},
|
|
186
|
+
data: {
|
|
187
|
+
db,
|
|
188
|
+
cursor: { field: 'id', direction: Direction.Desc },
|
|
189
|
+
},
|
|
190
|
+
enabled: process.env.NODE_ENV === 'development',
|
|
191
|
+
});
|
|
192
|
+
`,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
113
196
|
return files;
|
|
114
197
|
},
|
|
115
198
|
};
|