@geekmidas/cli 0.5.0 → 0.6.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/README.md +7 -7
- package/dist/{CronGenerator-BPTqNYOR.d.cts → CronGenerator-DWS3CCZt.d.cts} +4 -4
- package/dist/{CronGenerator-YAj59JUd.d.mts → CronGenerator-DZjdkEjI.d.mts} +4 -4
- package/dist/{EndpointGenerator-ChAD1INz.d.cts → EndpointGenerator-Dh7kMtuL.d.mts} +4 -4
- package/dist/{EndpointGenerator-Cj3O1U8-.d.mts → EndpointGenerator-zBsie_7s.d.cts} +4 -4
- package/dist/{FunctionGenerator-429-9NER.d.cts → FunctionGenerator-BmDHo27U.d.mts} +4 -4
- package/dist/{FunctionGenerator-BQ4ehoID.d.mts → FunctionGenerator-DXjXBxUd.d.cts} +4 -4
- package/dist/{Generator-BjHK_qce.d.mts → Generator-BGY-2dgI.d.cts} +3 -3
- package/dist/{Generator-DxQMCQp7.d.cts → Generator-yi9DH5TN.d.mts} +3 -3
- package/dist/{OpenApiTsGenerator-C4mHHaku.mjs → OpenApiTsGenerator-0ZDYWro5.mjs} +1 -1
- package/dist/{OpenApiTsGenerator-C4mHHaku.mjs.map → OpenApiTsGenerator-0ZDYWro5.mjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-Be-sKGTT.cjs → OpenApiTsGenerator-wLwpaq_I.cjs} +1 -1
- package/dist/{OpenApiTsGenerator-Be-sKGTT.cjs.map → OpenApiTsGenerator-wLwpaq_I.cjs.map} +1 -1
- package/dist/{SubscriberGenerator-7uX42xyG.d.mts → SubscriberGenerator-Bb-z3Kvx.d.cts} +4 -4
- package/dist/{SubscriberGenerator-Dtb3HS4i.d.cts → SubscriberGenerator-CwsXqCpS.d.mts} +4 -4
- package/dist/{api-BKIN0s0S.mjs → api-Bp5TIl1R.mjs} +29 -46
- package/dist/api-Bp5TIl1R.mjs.map +1 -0
- package/dist/{api-B3SCEHPf.cjs → api-D4W9-tdZ.cjs} +29 -46
- package/dist/api-D4W9-tdZ.cjs.map +1 -0
- package/dist/build/index.cjs +5 -3
- package/dist/build/index.d.cts +1 -1
- package/dist/build/index.d.mts +1 -1
- package/dist/build/index.mjs +5 -3
- package/dist/build/manifests.cjs +1 -1
- package/dist/build/manifests.d.cts +1 -1
- package/dist/build/manifests.d.mts +1 -1
- package/dist/build/manifests.mjs +1 -1
- package/dist/build/providerResolver.d.cts +1 -1
- package/dist/build/providerResolver.d.mts +1 -1
- package/dist/build/types.d.cts +2 -2
- package/dist/build/types.d.mts +2 -2
- package/dist/{build-B8C_qHir.mjs → build-BLriHKgm.mjs} +3 -3
- package/dist/{build-B8C_qHir.mjs.map → build-BLriHKgm.mjs.map} +1 -1
- package/dist/{build-D0Wr49bf.cjs → build-Z3yGHcy2.cjs} +3 -3
- package/dist/{build-D0Wr49bf.cjs.map → build-Z3yGHcy2.cjs.map} +1 -1
- package/dist/{config-CLEDqKO3.cjs → config-BP1IZynR.cjs} +17 -6
- package/dist/config-BP1IZynR.cjs.map +1 -0
- package/dist/{config-Ba-Gbpbc.d.cts → config-CIzRhm_D.d.mts} +2 -2
- package/dist/{config-DBsmMDhf.d.mts → config-CvehIYsb.d.cts} +2 -2
- package/dist/{config-Dp8RonV_.mjs → config-UCK12Lrr.mjs} +17 -6
- package/dist/config-UCK12Lrr.mjs.map +1 -0
- package/dist/config.d.cts +1 -1
- package/dist/config.d.mts +1 -1
- package/dist/dev/index.cjs +3 -1
- package/dist/dev/index.d.cts +2 -2
- package/dist/dev/index.d.mts +2 -2
- package/dist/dev/index.mjs +3 -1
- package/dist/{dev-B734w3L1.mjs → dev-BimlVcuk.mjs} +6 -1
- package/dist/dev-BimlVcuk.mjs.map +1 -0
- package/dist/{dev-DHqYn8k4.cjs → dev-Dcrb_ZSL.cjs} +6 -1
- package/dist/dev-Dcrb_ZSL.cjs.map +1 -0
- package/dist/{docker-5d8Yh5_X.cjs → docker-2-ipZDOJ.cjs} +1 -1
- package/dist/{docker-5d8Yh5_X.cjs.map → docker-2-ipZDOJ.cjs.map} +1 -1
- package/dist/{docker-DlUqdFle.mjs → docker-31GNwU3F.mjs} +1 -1
- package/dist/{docker-DlUqdFle.mjs.map → docker-31GNwU3F.mjs.map} +1 -1
- package/dist/{env-HfuJRlg5.d.cts → env-CQ3hXAAW.d.mts} +2 -2
- package/dist/{env-B-OKjgI4.cjs → env-CS0jvg7k.cjs} +1 -1
- package/dist/{env-B-OKjgI4.cjs.map → env-CS0jvg7k.cjs.map} +1 -1
- package/dist/{env-nd-iQPYM.d.mts → env-D4YFgMqo.d.cts} +2 -2
- package/dist/{env-tv1HlZlw.mjs → env-DEeVOvVu.mjs} +1 -1
- package/dist/{env-tv1HlZlw.mjs.map → env-DEeVOvVu.mjs.map} +1 -1
- package/dist/generators/CronGenerator.d.cts +4 -4
- package/dist/generators/CronGenerator.d.mts +4 -4
- package/dist/generators/EndpointGenerator.d.cts +4 -4
- package/dist/generators/EndpointGenerator.d.mts +4 -4
- package/dist/generators/FunctionGenerator.d.cts +4 -4
- package/dist/generators/FunctionGenerator.d.mts +4 -4
- package/dist/generators/Generator.d.cts +3 -3
- package/dist/generators/Generator.d.mts +3 -3
- package/dist/generators/OpenApiTsGenerator.cjs +1 -1
- package/dist/generators/OpenApiTsGenerator.mjs +1 -1
- package/dist/generators/SubscriberGenerator.d.cts +4 -4
- package/dist/generators/SubscriberGenerator.d.mts +4 -4
- package/dist/generators/index.d.cts +7 -7
- package/dist/generators/index.d.mts +7 -7
- package/dist/{index-C523No_B.d.mts → index-DG6xNQMH.d.cts} +25 -8
- package/dist/{index-DrzN4xkQ.d.cts → index-DZgrOOOW.d.mts} +25 -8
- package/dist/index.cjs +21 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +21 -21
- package/dist/index.mjs.map +1 -1
- package/dist/init/generators/config.cjs +1 -1
- package/dist/init/generators/config.d.cts +2 -2
- package/dist/init/generators/config.d.mts +2 -2
- package/dist/init/generators/config.mjs +1 -1
- package/dist/init/generators/docker.cjs +1 -1
- package/dist/init/generators/docker.d.cts +1 -1
- package/dist/init/generators/docker.d.mts +1 -1
- package/dist/init/generators/docker.mjs +1 -1
- package/dist/init/generators/env.cjs +1 -1
- package/dist/init/generators/env.d.cts +2 -2
- package/dist/init/generators/env.d.mts +2 -2
- package/dist/init/generators/env.mjs +1 -1
- package/dist/init/generators/index.cjs +9 -4
- package/dist/init/generators/index.d.cts +5 -5
- package/dist/init/generators/index.d.mts +5 -5
- package/dist/init/generators/index.mjs +9 -4
- package/dist/init/generators/models.cjs +1 -1
- package/dist/init/generators/models.d.cts +1 -1
- package/dist/init/generators/models.d.mts +1 -1
- package/dist/init/generators/models.mjs +1 -1
- package/dist/init/generators/monorepo.cjs +1 -1
- package/dist/init/generators/monorepo.d.cts +1 -1
- package/dist/init/generators/monorepo.d.mts +1 -1
- package/dist/init/generators/monorepo.mjs +1 -1
- package/dist/init/generators/package.cjs +6 -1
- package/dist/init/generators/package.d.cts +2 -2
- package/dist/init/generators/package.d.mts +2 -2
- package/dist/init/generators/package.mjs +6 -1
- package/dist/init/generators/source.cjs +1 -1
- package/dist/init/generators/source.d.cts +2 -2
- package/dist/init/generators/source.d.mts +2 -2
- package/dist/init/generators/source.mjs +1 -1
- package/dist/init/index.cjs +14 -14
- package/dist/init/index.d.cts +1 -1
- package/dist/init/index.d.mts +1 -1
- package/dist/init/index.mjs +14 -14
- package/dist/init/templates/api.cjs +1 -1
- package/dist/init/templates/api.d.cts +1 -1
- package/dist/init/templates/api.d.mts +1 -1
- package/dist/init/templates/api.mjs +1 -1
- package/dist/init/templates/index.cjs +8 -6
- package/dist/init/templates/index.d.cts +2 -2
- package/dist/init/templates/index.d.mts +2 -2
- package/dist/init/templates/index.mjs +6 -6
- package/dist/init/templates/minimal.cjs +1 -1
- package/dist/init/templates/minimal.d.cts +1 -1
- package/dist/init/templates/minimal.d.mts +1 -1
- package/dist/init/templates/minimal.mjs +1 -1
- package/dist/init/templates/serverless.cjs +1 -1
- package/dist/init/templates/serverless.d.cts +1 -1
- package/dist/init/templates/serverless.d.mts +1 -1
- package/dist/init/templates/serverless.mjs +1 -1
- package/dist/init/templates/worker.cjs +1 -1
- package/dist/init/templates/worker.d.cts +1 -1
- package/dist/init/templates/worker.d.mts +1 -1
- package/dist/init/templates/worker.mjs +1 -1
- package/dist/init/utils.cjs +1 -1
- package/dist/init/utils.mjs +1 -1
- package/dist/{init-CtOnZn3G.mjs → init-BMA7xi8r.mjs} +37 -21
- package/dist/init-BMA7xi8r.mjs.map +1 -0
- package/dist/{init-qLFsWR-R.cjs → init-D-7WEk-b.cjs} +37 -21
- package/dist/init-D-7WEk-b.cjs.map +1 -0
- package/dist/{manifests-DIA_2QYd.mjs → manifests-BNKG6AXf.mjs} +1 -1
- package/dist/{manifests-DIA_2QYd.mjs.map → manifests-BNKG6AXf.mjs.map} +1 -1
- package/dist/{manifests-VJ9-2JpW.cjs → manifests-D13Ej8AE.cjs} +1 -1
- package/dist/{manifests-VJ9-2JpW.cjs.map → manifests-D13Ej8AE.cjs.map} +1 -1
- package/dist/{minimal-C4GsE45s.mjs → minimal-BkyASH_C.mjs} +15 -9
- package/dist/minimal-BkyASH_C.mjs.map +1 -0
- package/dist/{minimal-Bdhhpp7v.cjs → minimal-CSFggzdH.cjs} +15 -9
- package/dist/minimal-CSFggzdH.cjs.map +1 -0
- package/dist/{models-cvNg6Oea.mjs → models-BWlDfviw.mjs} +1 -1
- package/dist/{models-cvNg6Oea.mjs.map → models-BWlDfviw.mjs.map} +1 -1
- package/dist/{models-DyNwdOcz.cjs → models-BapGSoHC.cjs} +1 -1
- package/dist/{models-DyNwdOcz.cjs.map → models-BapGSoHC.cjs.map} +1 -1
- package/dist/{monorepo-Cknwzj5C.mjs → monorepo-BBOWhkcd.mjs} +1 -1
- package/dist/{monorepo-Cknwzj5C.mjs.map → monorepo-BBOWhkcd.mjs.map} +1 -1
- package/dist/{monorepo-sEK8gW59.cjs → monorepo-CFtxHeDh.cjs} +1 -1
- package/dist/{monorepo-sEK8gW59.cjs.map → monorepo-CFtxHeDh.cjs.map} +1 -1
- package/dist/openapi-CTae4ybf.cjs +98 -0
- package/dist/openapi-CTae4ybf.cjs.map +1 -0
- package/dist/openapi-Dn9MeKg3.mjs +74 -0
- package/dist/openapi-Dn9MeKg3.mjs.map +1 -0
- package/dist/{openapi-react-query-DxHjXQvg.cjs → openapi-react-query-Cp-w8_05.cjs} +1 -1
- package/dist/{openapi-react-query-DxHjXQvg.cjs.map → openapi-react-query-Cp-w8_05.cjs.map} +1 -1
- package/dist/{openapi-react-query-o7Mp1Jd5.mjs → openapi-react-query-_-B3s8v_.mjs} +1 -1
- package/dist/{openapi-react-query-o7Mp1Jd5.mjs.map → openapi-react-query-_-B3s8v_.mjs.map} +1 -1
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.cjs +6 -3
- package/dist/openapi.d.cts +23 -3
- package/dist/openapi.d.mts +23 -3
- package/dist/openapi.mjs +3 -3
- package/dist/{package-C7WhWU8m.d.mts → package-6h-7QfJZ.d.cts} +2 -2
- package/dist/{package-DvWEMz6z.d.cts → package-BCe_KvGv.d.mts} +2 -2
- package/dist/{package-CIfmeuSW.mjs → package-C3If80n1.mjs} +7 -1
- package/dist/package-C3If80n1.mjs.map +1 -0
- package/dist/{package-PP-o1nvq.cjs → package-Dk8IMBOB.cjs} +6 -1
- package/dist/package-Dk8IMBOB.cjs.map +1 -0
- package/dist/{serverless-Yav3GRVz.cjs → serverless-AGOS-l3G.cjs} +15 -10
- package/dist/serverless-AGOS-l3G.cjs.map +1 -0
- package/dist/{serverless-DkHBF2vC.mjs → serverless-D5HjJByU.mjs} +15 -10
- package/dist/serverless-D5HjJByU.mjs.map +1 -0
- package/dist/{source-DT5Xhiob.cjs → source-C1cyfHcF.cjs} +1 -1
- package/dist/{source-DT5Xhiob.cjs.map → source-C1cyfHcF.cjs.map} +1 -1
- package/dist/{source-D6v2BnKT.d.mts → source-C3LiNUV9.d.mts} +2 -2
- package/dist/{source-DnaH_MLA.mjs → source-CkQHBpwu.mjs} +1 -1
- package/dist/{source-DnaH_MLA.mjs.map → source-CkQHBpwu.mjs.map} +1 -1
- package/dist/{source-D8fK9qRo.d.cts → source-Dtcjbokc.d.cts} +2 -2
- package/dist/templates-C0EMmhwb.mjs +88 -0
- package/dist/templates-C0EMmhwb.mjs.map +1 -0
- package/dist/templates-CbgQ9dw0.cjs +123 -0
- package/dist/templates-CbgQ9dw0.cjs.map +1 -0
- package/dist/{types-Cxl8-uwV.d.mts → types-Bi7VzDUZ.d.mts} +31 -2
- package/dist/{types-C4KITv-y.d.mts → types-D2xYkOal.d.mts} +2 -2
- package/dist/{types-DLFN49M3.d.cts → types-DA-r8HWZ.d.cts} +2 -2
- package/dist/{types-DB99_qIy.d.cts → types-KmjzMgu8.d.cts} +31 -2
- package/dist/types.d.cts +2 -2
- package/dist/types.d.mts +2 -2
- package/dist/{utils-C31-SWHP.mjs → utils-CKEzCxc1.mjs} +1 -1
- package/dist/{utils-C31-SWHP.mjs.map → utils-CKEzCxc1.mjs.map} +1 -1
- package/dist/{utils-BX3F4fT8.cjs → utils-DSdN2MTt.cjs} +1 -1
- package/dist/{utils-BX3F4fT8.cjs.map → utils-DSdN2MTt.cjs.map} +1 -1
- package/dist/{worker--8O5a3Hv.cjs → worker-CGhlqNH-.cjs} +15 -9
- package/dist/worker-CGhlqNH-.cjs.map +1 -0
- package/dist/{worker-Jme7uOOJ.mjs → worker-CiP420As.mjs} +15 -9
- package/dist/worker-CiP420As.mjs.map +1 -0
- package/examples/gkm.config.ts +3 -5
- package/package.json +4 -4
- package/src/__tests__/openapi.spec.ts +395 -302
- package/src/dev/__tests__/index.spec.ts +114 -119
- package/src/dev/index.ts +18 -0
- package/src/init/generators/config.ts +23 -5
- package/src/init/generators/package.ts +11 -4
- package/src/init/index.ts +26 -7
- package/src/init/templates/api.ts +38 -56
- package/src/init/templates/index.ts +46 -11
- package/src/init/templates/minimal.ts +23 -10
- package/src/init/templates/serverless.ts +23 -11
- package/src/init/templates/worker.ts +23 -10
- package/src/openapi.ts +83 -45
- package/src/types.ts +30 -0
- package/dist/api-B3SCEHPf.cjs.map +0 -1
- package/dist/api-BKIN0s0S.mjs.map +0 -1
- package/dist/config-CLEDqKO3.cjs.map +0 -1
- package/dist/config-Dp8RonV_.mjs.map +0 -1
- package/dist/dev-B734w3L1.mjs.map +0 -1
- package/dist/dev-DHqYn8k4.cjs.map +0 -1
- package/dist/init-CtOnZn3G.mjs.map +0 -1
- package/dist/init-qLFsWR-R.cjs.map +0 -1
- package/dist/minimal-Bdhhpp7v.cjs.map +0 -1
- package/dist/minimal-C4GsE45s.mjs.map +0 -1
- package/dist/openapi-BQWPWyNB.cjs +0 -56
- package/dist/openapi-BQWPWyNB.cjs.map +0 -1
- package/dist/openapi-DBX8cJJ8.mjs +0 -50
- package/dist/openapi-DBX8cJJ8.mjs.map +0 -1
- package/dist/package-CIfmeuSW.mjs.map +0 -1
- package/dist/package-PP-o1nvq.cjs.map +0 -1
- package/dist/serverless-DkHBF2vC.mjs.map +0 -1
- package/dist/serverless-Yav3GRVz.cjs.map +0 -1
- package/dist/templates-CBFUwpBy.mjs +0 -64
- package/dist/templates-CBFUwpBy.mjs.map +0 -1
- package/dist/templates-DM_rtYYW.cjs +0 -87
- package/dist/templates-DM_rtYYW.cjs.map +0 -1
- package/dist/worker--8O5a3Hv.cjs.map +0 -1
- package/dist/worker-Jme7uOOJ.mjs.map +0 -1
|
@@ -1,15 +1,37 @@
|
|
|
1
|
+
import type { AddressInfo } from 'node:net';
|
|
1
2
|
import { createServer } from 'node:net';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
4
|
import {
|
|
4
5
|
findAvailablePort,
|
|
5
6
|
isPortAvailable,
|
|
6
7
|
normalizeTelescopeConfig,
|
|
7
8
|
} from '../index';
|
|
8
9
|
|
|
10
|
+
// Track servers to clean up after each test
|
|
11
|
+
const activeServers: ReturnType<typeof createServer>[] = [];
|
|
12
|
+
|
|
13
|
+
afterEach(async () => {
|
|
14
|
+
// Close all servers and wait for them to fully close
|
|
15
|
+
await Promise.all(
|
|
16
|
+
activeServers.map(
|
|
17
|
+
(server) =>
|
|
18
|
+
new Promise<void>((resolve) => {
|
|
19
|
+
server.close(() => resolve());
|
|
20
|
+
}),
|
|
21
|
+
),
|
|
22
|
+
);
|
|
23
|
+
activeServers.length = 0;
|
|
24
|
+
// Give OS time to release ports
|
|
25
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
26
|
+
});
|
|
27
|
+
|
|
9
28
|
/**
|
|
10
|
-
* Helper to occupy a port for testing
|
|
29
|
+
* Helper to occupy a port for testing.
|
|
30
|
+
* Pass port 0 to get a random available port.
|
|
11
31
|
*/
|
|
12
|
-
function occupyPort(
|
|
32
|
+
function occupyPort(
|
|
33
|
+
port: number,
|
|
34
|
+
): Promise<{ server: ReturnType<typeof createServer>; port: number }> {
|
|
13
35
|
return new Promise((resolve, reject) => {
|
|
14
36
|
const server = createServer();
|
|
15
37
|
|
|
@@ -18,7 +40,9 @@ function occupyPort(port: number): Promise<ReturnType<typeof createServer>> {
|
|
|
18
40
|
});
|
|
19
41
|
|
|
20
42
|
server.once('listening', () => {
|
|
21
|
-
|
|
43
|
+
activeServers.push(server);
|
|
44
|
+
const actualPort = (server.address() as AddressInfo).port;
|
|
45
|
+
resolve({ server, port: actualPort });
|
|
22
46
|
});
|
|
23
47
|
|
|
24
48
|
server.listen(port);
|
|
@@ -28,127 +52,99 @@ function occupyPort(port: number): Promise<ReturnType<typeof createServer>> {
|
|
|
28
52
|
describe('Port Availability Functions', () => {
|
|
29
53
|
describe('isPortAvailable', () => {
|
|
30
54
|
it('should return true for an available port', async () => {
|
|
31
|
-
//
|
|
32
|
-
const port =
|
|
55
|
+
// Get a random port, close it, then check availability
|
|
56
|
+
const { server, port } = await occupyPort(0);
|
|
57
|
+
server.close();
|
|
58
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
59
|
+
|
|
33
60
|
const available = await isPortAvailable(port);
|
|
34
61
|
expect(available).toBe(true);
|
|
35
62
|
});
|
|
36
63
|
|
|
37
64
|
it('should return false for a port in use', async () => {
|
|
38
|
-
const port =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
expect(available).toBe(false);
|
|
44
|
-
} finally {
|
|
45
|
-
server.close();
|
|
46
|
-
}
|
|
65
|
+
const { port } = await occupyPort(0);
|
|
66
|
+
|
|
67
|
+
const available = await isPortAvailable(port);
|
|
68
|
+
expect(available).toBe(false);
|
|
69
|
+
// Server cleanup handled by afterEach
|
|
47
70
|
});
|
|
48
71
|
|
|
49
72
|
it('should handle multiple sequential checks correctly', async () => {
|
|
50
|
-
|
|
73
|
+
// Get a port to test with
|
|
74
|
+
const { server: tempServer, port } = await occupyPort(0);
|
|
75
|
+
tempServer.close();
|
|
76
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
51
77
|
|
|
52
78
|
// First check - port should be available
|
|
53
79
|
const firstCheck = await isPortAvailable(port);
|
|
54
80
|
expect(firstCheck).toBe(true);
|
|
55
81
|
|
|
56
82
|
// Occupy the port
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
} finally {
|
|
64
|
-
server.close();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Give a moment for the port to be released
|
|
68
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
69
|
-
|
|
70
|
-
// Third check - port should be available again
|
|
71
|
-
const thirdCheck = await isPortAvailable(port);
|
|
72
|
-
expect(thirdCheck).toBe(true);
|
|
83
|
+
await occupyPort(port);
|
|
84
|
+
|
|
85
|
+
// Second check - port should be unavailable
|
|
86
|
+
const secondCheck = await isPortAvailable(port);
|
|
87
|
+
expect(secondCheck).toBe(false);
|
|
88
|
+
// Server cleanup and third check handled by afterEach
|
|
73
89
|
});
|
|
74
90
|
});
|
|
75
91
|
|
|
76
92
|
describe('findAvailablePort', () => {
|
|
77
93
|
it('should return the preferred port if available', async () => {
|
|
78
|
-
|
|
94
|
+
// Get a random port, close it, then use as preferred
|
|
95
|
+
const { server, port: preferredPort } = await occupyPort(0);
|
|
96
|
+
server.close();
|
|
97
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
98
|
+
|
|
79
99
|
const foundPort = await findAvailablePort(preferredPort);
|
|
80
100
|
expect(foundPort).toBe(preferredPort);
|
|
81
101
|
});
|
|
82
102
|
|
|
83
103
|
it('should return the next available port if preferred is in use', async () => {
|
|
84
|
-
const preferredPort =
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const foundPort = await findAvailablePort(preferredPort);
|
|
89
|
-
expect(foundPort).toBe(preferredPort + 1);
|
|
90
|
-
} finally {
|
|
91
|
-
server.close();
|
|
92
|
-
}
|
|
104
|
+
const { port: preferredPort } = await occupyPort(0);
|
|
105
|
+
|
|
106
|
+
const foundPort = await findAvailablePort(preferredPort);
|
|
107
|
+
expect(foundPort).toBe(preferredPort + 1);
|
|
93
108
|
});
|
|
94
109
|
|
|
95
110
|
it('should skip multiple occupied ports', async () => {
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
} finally {
|
|
105
|
-
server1.close();
|
|
106
|
-
server2.close();
|
|
107
|
-
server3.close();
|
|
108
|
-
}
|
|
111
|
+
// Get a base port
|
|
112
|
+
const { port: basePort } = await occupyPort(0);
|
|
113
|
+
// Occupy consecutive ports
|
|
114
|
+
await occupyPort(basePort + 1);
|
|
115
|
+
await occupyPort(basePort + 2);
|
|
116
|
+
|
|
117
|
+
const foundPort = await findAvailablePort(basePort);
|
|
118
|
+
expect(foundPort).toBe(basePort + 3);
|
|
109
119
|
});
|
|
110
120
|
|
|
111
121
|
it('should throw error if no available port found within max attempts', async () => {
|
|
112
|
-
const preferredPort =
|
|
122
|
+
const { port: preferredPort } = await occupyPort(0);
|
|
113
123
|
const maxAttempts = 3;
|
|
114
124
|
|
|
115
|
-
// Occupy ports
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
findAvailablePort(preferredPort, maxAttempts),
|
|
125
|
-
).rejects.toThrow(
|
|
126
|
-
`Could not find an available port after trying ${maxAttempts} ports starting from ${preferredPort}`,
|
|
127
|
-
);
|
|
128
|
-
} finally {
|
|
129
|
-
servers.forEach((server) => server.close());
|
|
130
|
-
}
|
|
125
|
+
// Occupy consecutive ports
|
|
126
|
+
await occupyPort(preferredPort + 1);
|
|
127
|
+
await occupyPort(preferredPort + 2);
|
|
128
|
+
|
|
129
|
+
await expect(
|
|
130
|
+
findAvailablePort(preferredPort, maxAttempts),
|
|
131
|
+
).rejects.toThrow(
|
|
132
|
+
`Could not find an available port after trying ${maxAttempts} ports starting from ${preferredPort}`,
|
|
133
|
+
);
|
|
131
134
|
});
|
|
132
135
|
|
|
133
136
|
it('should respect custom maxAttempts parameter', async () => {
|
|
134
|
-
const preferredPort =
|
|
137
|
+
const { port: preferredPort } = await occupyPort(0);
|
|
135
138
|
const maxAttempts = 5;
|
|
136
139
|
|
|
137
|
-
// Occupy
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
try {
|
|
146
|
-
const foundPort = await findAvailablePort(preferredPort, maxAttempts);
|
|
147
|
-
// Should find port at preferredPort + 4 (within 5 attempts)
|
|
148
|
-
expect(foundPort).toBe(preferredPort + 4);
|
|
149
|
-
} finally {
|
|
150
|
-
servers.forEach((server) => server.close());
|
|
151
|
-
}
|
|
140
|
+
// Occupy consecutive ports (4 total including base)
|
|
141
|
+
await occupyPort(preferredPort + 1);
|
|
142
|
+
await occupyPort(preferredPort + 2);
|
|
143
|
+
await occupyPort(preferredPort + 3);
|
|
144
|
+
|
|
145
|
+
const foundPort = await findAvailablePort(preferredPort, maxAttempts);
|
|
146
|
+
// Should find port at preferredPort + 4 (within 5 attempts)
|
|
147
|
+
expect(foundPort).toBe(preferredPort + 4);
|
|
152
148
|
});
|
|
153
149
|
});
|
|
154
150
|
});
|
|
@@ -156,58 +152,57 @@ describe('Port Availability Functions', () => {
|
|
|
156
152
|
describe('DevServer', () => {
|
|
157
153
|
describe('port selection', () => {
|
|
158
154
|
it('should use requested port when available', async () => {
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
155
|
+
// Get a random port, close it, then use as requested
|
|
156
|
+
const { server, port: requestedPort } = await occupyPort(0);
|
|
157
|
+
server.close();
|
|
158
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
159
|
+
|
|
162
160
|
const actualPort = await findAvailablePort(requestedPort);
|
|
163
161
|
expect(actualPort).toBe(requestedPort);
|
|
164
162
|
});
|
|
165
163
|
|
|
166
164
|
it('should select alternative port when requested is in use', async () => {
|
|
167
|
-
const requestedPort =
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
expect(actualPort).toBeGreaterThan(requestedPort);
|
|
174
|
-
expect(actualPort).toBeLessThanOrEqual(requestedPort + 10);
|
|
175
|
-
} finally {
|
|
176
|
-
server.close();
|
|
177
|
-
}
|
|
165
|
+
const { port: requestedPort } = await occupyPort(0);
|
|
166
|
+
|
|
167
|
+
const actualPort = await findAvailablePort(requestedPort);
|
|
168
|
+
expect(actualPort).not.toBe(requestedPort);
|
|
169
|
+
expect(actualPort).toBeGreaterThan(requestedPort);
|
|
170
|
+
expect(actualPort).toBeLessThanOrEqual(requestedPort + 10);
|
|
178
171
|
});
|
|
179
172
|
});
|
|
180
173
|
});
|
|
181
174
|
|
|
182
175
|
describe('devCommand edge cases', () => {
|
|
183
176
|
it('should handle port conflicts gracefully', async () => {
|
|
184
|
-
const port =
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const alternativePort = await findAvailablePort(port);
|
|
190
|
-
expect(alternativePort).toBeGreaterThan(port);
|
|
191
|
-
} finally {
|
|
192
|
-
server.close();
|
|
193
|
-
}
|
|
177
|
+
const { port } = await occupyPort(0);
|
|
178
|
+
|
|
179
|
+
// The dev command should find an alternative port
|
|
180
|
+
const alternativePort = await findAvailablePort(port);
|
|
181
|
+
expect(alternativePort).toBeGreaterThan(port);
|
|
194
182
|
});
|
|
195
183
|
|
|
196
184
|
it('should handle concurrent port checks', async () => {
|
|
197
|
-
|
|
185
|
+
// Get three random available ports as base
|
|
186
|
+
const { server: s1, port: p1 } = await occupyPort(0);
|
|
187
|
+
const { server: s2, port: p2 } = await occupyPort(0);
|
|
188
|
+
const { server: s3, port: p3 } = await occupyPort(0);
|
|
189
|
+
s1.close();
|
|
190
|
+
s2.close();
|
|
191
|
+
s3.close();
|
|
192
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
198
193
|
|
|
199
194
|
// Run multiple port checks concurrently
|
|
200
195
|
const results = await Promise.all([
|
|
201
|
-
findAvailablePort(
|
|
202
|
-
findAvailablePort(
|
|
203
|
-
findAvailablePort(
|
|
196
|
+
findAvailablePort(p1),
|
|
197
|
+
findAvailablePort(p2),
|
|
198
|
+
findAvailablePort(p3),
|
|
204
199
|
]);
|
|
205
200
|
|
|
206
201
|
// All should succeed and return valid ports
|
|
207
202
|
expect(results).toHaveLength(3);
|
|
208
|
-
results.
|
|
209
|
-
|
|
210
|
-
|
|
203
|
+
expect(results[0]).toBe(p1);
|
|
204
|
+
expect(results[1]).toBe(p2);
|
|
205
|
+
expect(results[2]).toBe(p3);
|
|
211
206
|
});
|
|
212
207
|
});
|
|
213
208
|
|
package/src/dev/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
FunctionGenerator,
|
|
16
16
|
SubscriberGenerator,
|
|
17
17
|
} from '../generators';
|
|
18
|
+
import { generateOpenApi, resolveOpenApiConfig } from '../openapi';
|
|
18
19
|
import type {
|
|
19
20
|
GkmConfig,
|
|
20
21
|
LegacyProvider,
|
|
@@ -206,6 +207,12 @@ export async function devCommand(options: DevOptions): Promise<void> {
|
|
|
206
207
|
logger.log(`🔭 Telescope enabled at ${telescope.path}`);
|
|
207
208
|
}
|
|
208
209
|
|
|
210
|
+
// Resolve OpenAPI configuration
|
|
211
|
+
const openApiConfig = resolveOpenApiConfig(config);
|
|
212
|
+
if (openApiConfig.enabled) {
|
|
213
|
+
logger.log(`📄 OpenAPI output: ${openApiConfig.output}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
209
216
|
const buildContext: BuildContext = {
|
|
210
217
|
envParserPath,
|
|
211
218
|
envParserImportPattern,
|
|
@@ -222,6 +229,11 @@ export async function devCommand(options: DevOptions): Promise<void> {
|
|
|
222
229
|
resolved.enableOpenApi,
|
|
223
230
|
);
|
|
224
231
|
|
|
232
|
+
// Generate OpenAPI spec on startup
|
|
233
|
+
if (openApiConfig.enabled) {
|
|
234
|
+
await generateOpenApi(config);
|
|
235
|
+
}
|
|
236
|
+
|
|
225
237
|
// Determine runtime (default to node)
|
|
226
238
|
const runtime: Runtime = config.runtime ?? 'node';
|
|
227
239
|
|
|
@@ -307,6 +319,12 @@ export async function devCommand(options: DevOptions): Promise<void> {
|
|
|
307
319
|
resolved.providers[0] as LegacyProvider,
|
|
308
320
|
resolved.enableOpenApi,
|
|
309
321
|
);
|
|
322
|
+
|
|
323
|
+
// Regenerate OpenAPI if enabled
|
|
324
|
+
if (openApiConfig.enabled) {
|
|
325
|
+
await generateOpenApi(config, { silent: true });
|
|
326
|
+
}
|
|
327
|
+
|
|
310
328
|
logger.log('✅ Rebuild complete, restarting server...');
|
|
311
329
|
await devServer.restart();
|
|
312
330
|
} catch (error) {
|
|
@@ -11,17 +11,29 @@ export function generateConfigFiles(
|
|
|
11
11
|
options: TemplateOptions,
|
|
12
12
|
template: TemplateConfig,
|
|
13
13
|
): GeneratedFile[] {
|
|
14
|
-
const { telescope } = options;
|
|
14
|
+
const { telescope, routesStructure } = options;
|
|
15
15
|
const isServerless = template.name === 'serverless';
|
|
16
16
|
const hasWorker = template.name === 'worker';
|
|
17
17
|
|
|
18
|
+
// Get routes glob pattern based on structure
|
|
19
|
+
const getRoutesGlob = () => {
|
|
20
|
+
switch (routesStructure) {
|
|
21
|
+
case 'centralized-endpoints':
|
|
22
|
+
return './src/endpoints/**/*.ts';
|
|
23
|
+
case 'centralized-routes':
|
|
24
|
+
return './src/routes/**/*.ts';
|
|
25
|
+
case 'domain-based':
|
|
26
|
+
return './src/**/routes/*.ts';
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
18
30
|
// Build gkm.config.ts
|
|
19
|
-
let gkmConfig = `import { defineConfig } from '@geekmidas/cli';
|
|
31
|
+
let gkmConfig = `import { defineConfig } from '@geekmidas/cli/config';
|
|
20
32
|
|
|
21
33
|
export default defineConfig({
|
|
22
|
-
routes: '
|
|
23
|
-
envParser: './src/config/env',
|
|
24
|
-
logger: './src/config/logger',`;
|
|
34
|
+
routes: '${getRoutesGlob()}',
|
|
35
|
+
envParser: './src/config/env#envParser',
|
|
36
|
+
logger: './src/config/logger#logger',`;
|
|
25
37
|
|
|
26
38
|
if (isServerless || hasWorker) {
|
|
27
39
|
gkmConfig += `
|
|
@@ -42,6 +54,12 @@ export default defineConfig({
|
|
|
42
54
|
},`;
|
|
43
55
|
}
|
|
44
56
|
|
|
57
|
+
// Always add openapi config (output path is fixed to .gkm/openapi.ts)
|
|
58
|
+
gkmConfig += `
|
|
59
|
+
openapi: {
|
|
60
|
+
enabled: true,
|
|
61
|
+
},`;
|
|
62
|
+
|
|
45
63
|
gkmConfig += `
|
|
46
64
|
});
|
|
47
65
|
`;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
GeneratedFile,
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
type GeneratedFile,
|
|
3
|
+
OPENAPI_OUTPUT_PATH,
|
|
4
|
+
type TemplateConfig,
|
|
5
|
+
type TemplateOptions,
|
|
5
6
|
} from '../templates/index.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -67,6 +68,12 @@ export function generatePackageJson(
|
|
|
67
68
|
version: '0.0.1',
|
|
68
69
|
private: true,
|
|
69
70
|
type: 'module',
|
|
71
|
+
exports: {
|
|
72
|
+
'./client': {
|
|
73
|
+
types: OPENAPI_OUTPUT_PATH,
|
|
74
|
+
import: OPENAPI_OUTPUT_PATH,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
70
77
|
scripts,
|
|
71
78
|
dependencies: sortObject(dependencies),
|
|
72
79
|
devDependencies: sortObject(devDependencies),
|
package/src/init/index.ts
CHANGED
|
@@ -13,7 +13,8 @@ import {
|
|
|
13
13
|
type TemplateName,
|
|
14
14
|
type TemplateOptions,
|
|
15
15
|
getTemplate,
|
|
16
|
-
|
|
16
|
+
loggerTypeChoices,
|
|
17
|
+
routesStructureChoices,
|
|
17
18
|
templateChoices,
|
|
18
19
|
} from './templates/index.js';
|
|
19
20
|
import {
|
|
@@ -85,9 +86,16 @@ export async function initCommand(
|
|
|
85
86
|
},
|
|
86
87
|
{
|
|
87
88
|
type: options.yes ? null : 'select',
|
|
88
|
-
name: '
|
|
89
|
-
message: '
|
|
90
|
-
choices:
|
|
89
|
+
name: 'loggerType',
|
|
90
|
+
message: 'Logger:',
|
|
91
|
+
choices: loggerTypeChoices,
|
|
92
|
+
initial: 0,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
type: options.yes ? null : 'select',
|
|
96
|
+
name: 'routesStructure',
|
|
97
|
+
message: 'Routes structure:',
|
|
98
|
+
choices: routesStructureChoices,
|
|
91
99
|
initial: 0,
|
|
92
100
|
},
|
|
93
101
|
{
|
|
@@ -137,9 +145,10 @@ export async function initCommand(
|
|
|
137
145
|
template: options.template || answers.template || 'minimal',
|
|
138
146
|
telescope: options.yes ? true : (answers.telescope ?? true),
|
|
139
147
|
database: options.yes ? true : (answers.database ?? true),
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
148
|
+
loggerType: options.yes ? 'pino' : (answers.loggerType ?? 'pino'),
|
|
149
|
+
routesStructure: options.yes
|
|
150
|
+
? 'centralized-endpoints'
|
|
151
|
+
: (answers.routesStructure ?? 'centralized-endpoints'),
|
|
143
152
|
monorepo,
|
|
144
153
|
apiPath: monorepo ? (options.apiPath ?? answers.apiPath ?? 'apps/api') : '',
|
|
145
154
|
};
|
|
@@ -199,6 +208,16 @@ export async function initCommand(
|
|
|
199
208
|
} catch {
|
|
200
209
|
console.error('\n Warning: Failed to install dependencies.');
|
|
201
210
|
}
|
|
211
|
+
|
|
212
|
+
// Format generated files with biome
|
|
213
|
+
try {
|
|
214
|
+
execSync('npx @biomejs/biome format --write --unsafe .', {
|
|
215
|
+
cwd: targetDir,
|
|
216
|
+
stdio: 'inherit',
|
|
217
|
+
});
|
|
218
|
+
} catch {
|
|
219
|
+
// Silently ignore format errors
|
|
220
|
+
}
|
|
202
221
|
}
|
|
203
222
|
|
|
204
223
|
// Print next steps
|
|
@@ -41,6 +41,30 @@ export const apiTemplate: TemplateConfig = {
|
|
|
41
41
|
},
|
|
42
42
|
|
|
43
43
|
files: (options: TemplateOptions): GeneratedFile[] => {
|
|
44
|
+
const { loggerType, routesStructure } = options;
|
|
45
|
+
|
|
46
|
+
const loggerContent = `import { createLogger } from '@geekmidas/logger/${loggerType}';
|
|
47
|
+
|
|
48
|
+
export const logger = createLogger();
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
// Get route path based on structure
|
|
52
|
+
const getRoutePath = (file: string) => {
|
|
53
|
+
switch (routesStructure) {
|
|
54
|
+
case 'centralized-endpoints':
|
|
55
|
+
return `src/endpoints/${file}`;
|
|
56
|
+
case 'centralized-routes':
|
|
57
|
+
return `src/routes/${file}`;
|
|
58
|
+
case 'domain-based': {
|
|
59
|
+
const parts = file.split('/');
|
|
60
|
+
if (parts.length === 1) {
|
|
61
|
+
return `src/${file.replace('.ts', '')}/routes/index.ts`;
|
|
62
|
+
}
|
|
63
|
+
return `src/${parts[0]}/routes/${parts.slice(1).join('/')}`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
44
68
|
const files: GeneratedFile[] = [
|
|
45
69
|
// src/config/env.ts
|
|
46
70
|
{
|
|
@@ -66,21 +90,15 @@ export const config = envParser
|
|
|
66
90
|
`,
|
|
67
91
|
},
|
|
68
92
|
|
|
69
|
-
// src/config/logger.ts
|
|
93
|
+
// src/config/logger.ts
|
|
70
94
|
{
|
|
71
95
|
path: 'src/config/logger.ts',
|
|
72
|
-
content:
|
|
73
|
-
|
|
74
|
-
export const logger = new PinoLogger({
|
|
75
|
-
app: '${options.name}',
|
|
76
|
-
level: process.env.LOG_LEVEL || 'info',
|
|
77
|
-
});
|
|
78
|
-
`,
|
|
96
|
+
content: loggerContent,
|
|
79
97
|
},
|
|
80
98
|
|
|
81
|
-
//
|
|
99
|
+
// health endpoint
|
|
82
100
|
{
|
|
83
|
-
path: '
|
|
101
|
+
path: getRoutePath('health.ts'),
|
|
84
102
|
content: `import { e } from '@geekmidas/constructs/endpoints';
|
|
85
103
|
|
|
86
104
|
export default e
|
|
@@ -91,46 +109,11 @@ export default e
|
|
|
91
109
|
}));
|
|
92
110
|
`,
|
|
93
111
|
},
|
|
94
|
-
];
|
|
95
|
-
|
|
96
|
-
// Add user endpoints based on route style
|
|
97
|
-
if (options.routeStyle === 'flat') {
|
|
98
|
-
files.push(
|
|
99
|
-
{
|
|
100
|
-
path: 'src/endpoints/users-list.ts',
|
|
101
|
-
content: `import { e } from '@geekmidas/constructs/endpoints';
|
|
102
|
-
|
|
103
|
-
export default e
|
|
104
|
-
.get('/users')
|
|
105
|
-
.handle(async () => ({
|
|
106
|
-
users: [
|
|
107
|
-
{ id: '1', name: 'Alice' },
|
|
108
|
-
{ id: '2', name: 'Bob' },
|
|
109
|
-
],
|
|
110
|
-
}));
|
|
111
|
-
`,
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
path: 'src/endpoints/users-get.ts',
|
|
115
|
-
content: `import { e } from '@geekmidas/constructs/endpoints';
|
|
116
|
-
import { z } from 'zod';
|
|
117
112
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
id: params.id,
|
|
123
|
-
name: 'Alice',
|
|
124
|
-
email: 'alice@example.com',
|
|
125
|
-
}));
|
|
126
|
-
`,
|
|
127
|
-
},
|
|
128
|
-
);
|
|
129
|
-
} else {
|
|
130
|
-
files.push(
|
|
131
|
-
{
|
|
132
|
-
path: 'src/endpoints/users/list.ts',
|
|
133
|
-
content: `import { e } from '@geekmidas/constructs/endpoints';
|
|
113
|
+
// users endpoints
|
|
114
|
+
{
|
|
115
|
+
path: getRoutePath('users/list.ts'),
|
|
116
|
+
content: `import { e } from '@geekmidas/constructs/endpoints';
|
|
134
117
|
|
|
135
118
|
export default e
|
|
136
119
|
.get('/users')
|
|
@@ -141,10 +124,10 @@ export default e
|
|
|
141
124
|
],
|
|
142
125
|
}));
|
|
143
126
|
`,
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
path: getRoutePath('users/get.ts'),
|
|
130
|
+
content: `import { e } from '@geekmidas/constructs/endpoints';
|
|
148
131
|
import { z } from 'zod';
|
|
149
132
|
|
|
150
133
|
export default e
|
|
@@ -156,9 +139,8 @@ export default e
|
|
|
156
139
|
email: 'alice@example.com',
|
|
157
140
|
}));
|
|
158
141
|
`,
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
142
|
+
},
|
|
143
|
+
];
|
|
162
144
|
|
|
163
145
|
// Add database service if enabled
|
|
164
146
|
if (options.database) {
|