@geekmidas/cli 10.0.0-alpha.71 → 10.0.0-alpha.73
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/{OpenApiTsGenerator-DZZwHdDC.cjs → EndpointGenerator-CP6sBvQV.cjs} +7 -513
- package/dist/EndpointGenerator-CP6sBvQV.cjs.map +1 -0
- package/dist/{OpenApiTsGenerator-Bhr_XWWN.mjs → EndpointGenerator-DftalLYg.mjs} +6 -512
- package/dist/EndpointGenerator-DftalLYg.mjs.map +1 -0
- package/dist/{LegacyStateStore-NSHEinQi.mjs → LegacyStateStore-DTo7z924.mjs} +2 -2
- package/dist/{LegacyStateStore-NSHEinQi.mjs.map → LegacyStateStore-DTo7z924.mjs.map} +1 -1
- package/dist/{LegacyStateStore-Dd5IwiyC.cjs → LegacyStateStore-D_78eaHM.cjs} +2 -2
- package/dist/{LegacyStateStore-Dd5IwiyC.cjs.map → LegacyStateStore-D_78eaHM.cjs.map} +1 -1
- package/dist/{LocalStateStore-DTylG4T4.cjs → LocalStateStore-BhrlQIXD.cjs} +2 -2
- package/dist/{LocalStateStore-DTylG4T4.cjs.map → LocalStateStore-BhrlQIXD.cjs.map} +1 -1
- package/dist/{LocalStateStore-DUoG2wA_.mjs → LocalStateStore-CNjgkRk4.mjs} +2 -2
- package/dist/{LocalStateStore-DUoG2wA_.mjs.map → LocalStateStore-CNjgkRk4.mjs.map} +1 -1
- package/dist/OpenApiTsGenerator-CKBCckKz.cjs +514 -0
- package/dist/OpenApiTsGenerator-CKBCckKz.cjs.map +1 -0
- package/dist/OpenApiTsGenerator-nu1r1NgB.mjs +509 -0
- package/dist/OpenApiTsGenerator-nu1r1NgB.mjs.map +1 -0
- package/dist/{S3StateStore-Dxob3PNA.mjs → S3StateStore-g2P7T_Mo.mjs} +2 -2
- package/dist/{S3StateStore-Dxob3PNA.mjs.map → S3StateStore-g2P7T_Mo.mjs.map} +1 -1
- package/dist/{S3StateStore-D7hrU-sy.cjs → S3StateStore-oCxRs6HM.cjs} +2 -2
- package/dist/{S3StateStore-D7hrU-sy.cjs.map → S3StateStore-oCxRs6HM.cjs.map} +1 -1
- package/dist/{SSMStateStore-Dvl4A8Up.mjs → SSMStateStore-CYenunmK.mjs} +2 -2
- package/dist/{SSMStateStore-Dvl4A8Up.mjs.map → SSMStateStore-CYenunmK.mjs.map} +1 -1
- package/dist/{SSMStateStore-DGIKJ4NN.cjs → SSMStateStore-DXp0m7XI.cjs} +2 -2
- package/dist/{SSMStateStore-DGIKJ4NN.cjs.map → SSMStateStore-DXp0m7XI.cjs.map} +1 -1
- package/dist/SubscriberGenerator-Bixm28fN.cjs +461 -0
- package/dist/SubscriberGenerator-Bixm28fN.cjs.map +1 -0
- package/dist/SubscriberGenerator-DZk_pKDh.mjs +432 -0
- package/dist/SubscriberGenerator-DZk_pKDh.mjs.map +1 -0
- package/dist/{add-XTLU19_E.mjs → add-BiPlBUq3.mjs} +4 -4
- package/dist/{add-XTLU19_E.mjs.map → add-BiPlBUq3.mjs.map} +1 -1
- package/dist/{add-DeC2Kyyv.cjs → add-C1ojB2Yy.cjs} +4 -4
- package/dist/{add-DeC2Kyyv.cjs.map → add-C1ojB2Yy.cjs.map} +1 -1
- package/dist/{bundler-CNTVLCCA.mjs → bundler-C2LZoLdP.mjs} +6 -6
- package/dist/bundler-C2LZoLdP.mjs.map +1 -0
- package/dist/{bundler-CEaR6Dpb.cjs → bundler-C35h0WKt.cjs} +6 -6
- package/dist/bundler-C35h0WKt.cjs.map +1 -0
- package/dist/{cli-D1arS0p1.mjs → cli-BfdfyTF6.mjs} +4 -4
- package/dist/{cli-D1arS0p1.mjs.map → cli-BfdfyTF6.mjs.map} +1 -1
- package/dist/{cli-MMgBrHx7.cjs → cli-DJVL2sL9.cjs} +4 -4
- package/dist/{cli-MMgBrHx7.cjs.map → cli-DJVL2sL9.cjs.map} +1 -1
- package/dist/{compose-T3hnnjKZ.mjs → compose-9II-0IsK.mjs} +221 -64
- package/dist/compose-9II-0IsK.mjs.map +1 -0
- package/dist/{compose-B-MME-4C.cjs → compose-CNnw8meq.cjs} +223 -66
- package/dist/compose-CNnw8meq.cjs.map +1 -0
- package/dist/{config-BN6iXvyR.d.mts → config-B8OR_TrO.d.mts} +3 -3
- package/dist/{config-BN6iXvyR.d.mts.map → config-B8OR_TrO.d.mts.map} +1 -1
- package/dist/{config-j9zBKaEG.d.cts → config-RLuR2o9A.d.cts} +3 -3
- package/dist/{config-j9zBKaEG.d.cts.map → config-RLuR2o9A.d.cts.map} +1 -1
- package/dist/config.cjs +4 -1
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +3 -3
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +4 -1
- package/dist/config.mjs.map +1 -1
- package/dist/{credentialSchemas-BmYxnEz_.mjs → credentialSchemas-BJqRJNK4.mjs} +2 -2
- package/dist/{credentialSchemas-BmYxnEz_.mjs.map → credentialSchemas-BJqRJNK4.mjs.map} +1 -1
- package/dist/{credentialSchemas-BcNL1N5R.cjs → credentialSchemas-DCR4Wd1W.cjs} +2 -2
- package/dist/{credentialSchemas-BcNL1N5R.cjs.map → credentialSchemas-DCR4Wd1W.cjs.map} +1 -1
- package/dist/deploy/public.cjs +7 -7
- package/dist/deploy/public.d.cts +4 -4
- package/dist/deploy/public.d.cts.map +1 -1
- package/dist/deploy/public.d.mts +4 -4
- package/dist/deploy/public.d.mts.map +1 -1
- package/dist/deploy/public.mjs +7 -7
- package/dist/deploy/sniffer-routes-worker.cjs +2 -2
- package/dist/deploy/sniffer-routes-worker.mjs +2 -2
- package/dist/{deploy-CZE0m6jd.cjs → deploy-BitZLdnY.cjs} +12 -6
- package/dist/deploy-BitZLdnY.cjs.map +1 -0
- package/dist/{deploy-ClC53rc6.mjs → deploy-nmov4j0B.mjs} +12 -6
- package/dist/deploy-nmov4j0B.mjs.map +1 -0
- package/dist/{discover-Buwf1z4P.mjs → discover-BPelzeGW.mjs} +21 -2
- package/dist/discover-BPelzeGW.mjs.map +1 -0
- package/dist/{discover-B_IkPukx.d.cts → discover-CU0paUyH.d.cts} +10 -1
- package/dist/discover-CU0paUyH.d.cts.map +1 -0
- package/dist/{discover-B4VjaFJk.cjs → discover-D1It5r_h.cjs} +21 -2
- package/dist/discover-D1It5r_h.cjs.map +1 -0
- package/dist/{discover-B6yPpvRV.d.mts → discover-DFiJxwfv.d.mts} +10 -1
- package/dist/discover-DFiJxwfv.d.mts.map +1 -0
- package/dist/{dokploy-D1UwQKm3.mjs → dokploy-CZ1vZt21.mjs} +2 -2
- package/dist/{dokploy-D1UwQKm3.mjs.map → dokploy-CZ1vZt21.mjs.map} +1 -1
- package/dist/{dokploy-lwmH05zr.cjs → dokploy-Ce_VTytN.cjs} +2 -2
- package/dist/{dokploy-lwmH05zr.cjs.map → dokploy-Ce_VTytN.cjs.map} +1 -1
- package/dist/{encryption-D05sNwED.cjs → encryption-CDGRdcMr.cjs} +2 -2
- package/dist/{encryption-D05sNwED.cjs.map → encryption-CDGRdcMr.cjs.map} +1 -1
- package/dist/{encryption-DcrxWl1s.mjs → encryption-CmfPICSN.mjs} +2 -2
- package/dist/{encryption-DcrxWl1s.mjs.map → encryption-CmfPICSN.mjs.map} +1 -1
- package/dist/{engine-DmQS3XHX.mjs → engine-B37qBO5W.mjs} +117 -17
- package/dist/engine-B37qBO5W.mjs.map +1 -0
- package/dist/{engine-Cry1vwMz.cjs → engine-DL2-PW6m.cjs} +117 -17
- package/dist/engine-DL2-PW6m.cjs.map +1 -0
- package/dist/{github-BchWoiVx.cjs → github-C6qF8kHg.cjs} +2 -2
- package/dist/{github-BchWoiVx.cjs.map → github-C6qF8kHg.cjs.map} +1 -1
- package/dist/{github-CqYOP8cI.mjs → github-hjHHm7jM.mjs} +2 -2
- package/dist/{github-CqYOP8cI.mjs.map → github-hjHHm7jM.mjs.map} +1 -1
- package/dist/{index-DvoxuTqR.d.mts → index-DHyTqGG3.d.mts} +2 -2
- package/dist/{index-Bk3NY6DN.d.cts.map → index-DHyTqGG3.d.mts.map} +1 -1
- package/dist/{index-Bk3NY6DN.d.cts → index-Dg-UZ4Hm.d.cts} +2 -2
- package/dist/{index-DvoxuTqR.d.mts.map → index-Dg-UZ4Hm.d.cts.map} +1 -1
- package/dist/index.cjs +124 -448
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +118 -442
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-guck0NJK.mjs → migrate-CFFC3esE.mjs} +3 -3
- package/dist/{migrate-guck0NJK.mjs.map → migrate-CFFC3esE.mjs.map} +1 -1
- package/dist/{migrate-qKeGYq21.cjs → migrate-kA_j1TPK.cjs} +3 -3
- package/dist/{migrate-qKeGYq21.cjs.map → migrate-kA_j1TPK.cjs.map} +1 -1
- package/dist/openapi.cjs +3 -2
- package/dist/openapi.cjs.map +1 -1
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +2 -1
- package/dist/openapi.mjs.map +1 -1
- package/dist/{ready-Dp6arkI4.cjs → ready-BIuMw6wH.cjs} +66 -289
- package/dist/ready-BIuMw6wH.cjs.map +1 -0
- package/dist/{ready-xpoX11uu.mjs → ready-C0U0NYfB.mjs} +58 -281
- package/dist/ready-C0U0NYfB.mjs.map +1 -0
- package/dist/reconcile/public.cjs +1 -1
- package/dist/reconcile/public.d.cts +2 -2
- package/dist/reconcile/public.d.mts +2 -2
- package/dist/reconcile/public.mjs +1 -1
- package/dist/{resolve-BsXEXAUz.mjs → resolve-CV4eQkDa.mjs} +3 -3
- package/dist/{resolve-BsXEXAUz.mjs.map → resolve-CV4eQkDa.mjs.map} +1 -1
- package/dist/{resolve-wEjEvbJ5.cjs → resolve-E5RfY0bm.cjs} +3 -3
- package/dist/{resolve-wEjEvbJ5.cjs.map → resolve-E5RfY0bm.cjs.map} +1 -1
- package/dist/{rollback-Bk91csFg.mjs → rollback-C-V-Rf4a.mjs} +4 -4
- package/dist/{rollback-Bk91csFg.mjs.map → rollback-C-V-Rf4a.mjs.map} +1 -1
- package/dist/{rollback-DmcU-efZ.cjs → rollback-QGwomD64.cjs} +4 -4
- package/dist/{rollback-DmcU-efZ.cjs.map → rollback-QGwomD64.cjs.map} +1 -1
- package/dist/{runtime-DHJhe4QI.d.mts → runtime-CWYLIeCU.d.mts} +2 -2
- package/dist/{runtime-CYAO4iZG.d.cts.map → runtime-CWYLIeCU.d.mts.map} +1 -1
- package/dist/{runtime-CYAO4iZG.d.cts → runtime-CmuQOT6A.d.cts} +2 -2
- package/dist/{runtime-DHJhe4QI.d.mts.map → runtime-CmuQOT6A.d.cts.map} +1 -1
- package/dist/sandbox/credentials-worker.cjs +2 -2
- package/dist/sandbox/credentials-worker.mjs +2 -2
- package/dist/sandbox/discover-worker.cjs +6 -3
- package/dist/sandbox/discover-worker.cjs.map +1 -1
- package/dist/sandbox/discover-worker.mjs +6 -3
- package/dist/sandbox/discover-worker.mjs.map +1 -1
- package/dist/sandbox/migrate-worker.cjs +1 -1
- package/dist/sandbox/migrate-worker.mjs +1 -1
- package/dist/{sniffer-vAXsU_4G.mjs → sniffer-BFmdZpZ2.mjs} +6 -6
- package/dist/{sniffer-vAXsU_4G.mjs.map → sniffer-BFmdZpZ2.mjs.map} +1 -1
- package/dist/{sniffer-DpHpFZBD.cjs → sniffer-C-T7dSMN.cjs} +6 -6
- package/dist/{sniffer-DpHpFZBD.cjs.map → sniffer-C-T7dSMN.cjs.map} +1 -1
- package/dist/{state-CCuTJNRH.cjs → state-DMjFPOhf.cjs} +942 -22
- package/dist/state-DMjFPOhf.cjs.map +1 -0
- package/dist/{state-BJFherj1.mjs → state-DtS2HEfi.mjs} +877 -17
- package/dist/state-DtS2HEfi.mjs.map +1 -0
- package/dist/target/public.cjs +1 -1
- package/dist/target/public.d.cts +2 -2
- package/dist/target/public.d.mts +2 -2
- package/dist/target/public.mjs +1 -1
- package/dist/{terminal-CFO3xWoF.cjs → terminal-BL5E0xbR.cjs} +2 -2
- package/dist/{terminal-CFO3xWoF.cjs.map → terminal-BL5E0xbR.cjs.map} +1 -1
- package/dist/{terminal-C6TBo_SA.mjs → terminal-BdvrmkaC.mjs} +2 -2
- package/dist/{terminal-C6TBo_SA.mjs.map → terminal-BdvrmkaC.mjs.map} +1 -1
- package/dist/{types-DkG5WW8l.d.mts → types-BLdCL4fB.d.mts} +17 -1
- package/dist/types-BLdCL4fB.d.mts.map +1 -0
- package/dist/{types-BD1jgA85.d.cts → types-DwCQCkC-.d.cts} +17 -1
- package/dist/types-DwCQCkC-.d.cts.map +1 -0
- package/dist/vitest.cjs +1 -1
- package/dist/vitest.mjs +1 -1
- package/dist/workspace/index.d.cts +2 -2
- package/dist/workspace/index.d.mts +2 -2
- package/dist/workspace-C7NqCsQF.mjs.map +1 -1
- package/dist/workspace-vdrcRI-m.cjs.map +1 -1
- package/package.json +15 -15
- package/dist/OpenApiTsGenerator-Bhr_XWWN.mjs.map +0 -1
- package/dist/OpenApiTsGenerator-DZZwHdDC.cjs.map +0 -1
- package/dist/bundler-CEaR6Dpb.cjs.map +0 -1
- package/dist/bundler-CNTVLCCA.mjs.map +0 -1
- package/dist/compose-B-MME-4C.cjs.map +0 -1
- package/dist/compose-T3hnnjKZ.mjs.map +0 -1
- package/dist/deploy-CZE0m6jd.cjs.map +0 -1
- package/dist/deploy-ClC53rc6.mjs.map +0 -1
- package/dist/discover-B4VjaFJk.cjs.map +0 -1
- package/dist/discover-B6yPpvRV.d.mts.map +0 -1
- package/dist/discover-B_IkPukx.d.cts.map +0 -1
- package/dist/discover-Buwf1z4P.mjs.map +0 -1
- package/dist/engine-Cry1vwMz.cjs.map +0 -1
- package/dist/engine-DmQS3XHX.mjs.map +0 -1
- package/dist/fakes-C9RiT6XG.mjs +0 -125
- package/dist/fakes-C9RiT6XG.mjs.map +0 -1
- package/dist/fakes-zW-jNAbQ.cjs +0 -148
- package/dist/fakes-zW-jNAbQ.cjs.map +0 -1
- package/dist/ready-Dp6arkI4.cjs.map +0 -1
- package/dist/ready-xpoX11uu.mjs.map +0 -1
- package/dist/state-BJFherj1.mjs.map +0 -1
- package/dist/state-CCuTJNRH.cjs.map +0 -1
- package/dist/types-BD1jgA85.d.cts.map +0 -1
- package/dist/types-DkG5WW8l.d.mts.map +0 -1
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
const require_discover = require("./discover-
|
|
1
|
+
const require_discover = require("./discover-D1It5r_h.cjs");
|
|
2
2
|
let node_path = require("node:path");
|
|
3
3
|
let node_fs = require("node:fs");
|
|
4
4
|
let node_fs_promises = require("node:fs/promises");
|
|
5
5
|
let _geekmidas_constructs_endpoints = require("@geekmidas/constructs/endpoints");
|
|
6
|
-
let _geekmidas_schema_conversion = require("@geekmidas/schema/conversion");
|
|
7
6
|
//#region src/generators/telemetry.ts
|
|
8
7
|
/**
|
|
9
8
|
* Every package `@geekmidas/telescope/instrumentation` loads at runtime.
|
|
@@ -496,9 +495,9 @@ const telescope =
|
|
|
496
495
|
storage: new InMemoryStorage({ maxEntries: ${context.telescope?.maxEntries} }),
|
|
497
496
|
});` : ""}
|
|
498
497
|
|
|
499
|
-
${context.storageDrivers?.setup ? `// Which
|
|
500
|
-
// each injected URL picks one, so no construct and no
|
|
501
|
-
// a provider.
|
|
498
|
+
${context.storageDrivers?.setup ? `// Which drivers exist — storage, cache, events — is the entry point's
|
|
499
|
+
// decision: the scheme in each injected URL picks one, so no construct and no
|
|
500
|
+
// application module names a provider.
|
|
502
501
|
${context.storageDrivers.setup}
|
|
503
502
|
` : ""}
|
|
504
503
|
${context.surface ? `// Who this server is to Postgres: application_name on every connection.
|
|
@@ -851,521 +850,16 @@ await start({
|
|
|
851
850
|
}
|
|
852
851
|
};
|
|
853
852
|
//#endregion
|
|
854
|
-
//#region src/generators/OpenApiTsGenerator.ts
|
|
855
|
-
/**
|
|
856
|
-
* Generates TypeScript OpenAPI module from endpoints.
|
|
857
|
-
* Outputs:
|
|
858
|
-
* - securitySchemes: typed security scheme definitions
|
|
859
|
-
* - endpointAuth: runtime map of endpoints to auth requirements
|
|
860
|
-
* - paths: TypeScript interface for type-safe fetcher
|
|
861
|
-
* - schema interfaces: reusable TypeScript types from Zod/Valibot schemas
|
|
862
|
-
*/
|
|
863
|
-
var OpenApiTsGenerator = class {
|
|
864
|
-
async generate(endpoints, options = {}) {
|
|
865
|
-
const { title = "API", version = "1.0.0", description } = options;
|
|
866
|
-
const endpointInfos = await this.extractEndpointInfos(endpoints);
|
|
867
|
-
const securitySchemes = this.collectSecuritySchemes(endpointInfos);
|
|
868
|
-
const endpointAuth = this.buildEndpointAuthMap(endpointInfos);
|
|
869
|
-
const schemaInterfaces = await this.generateSchemaInterfaces(endpointInfos);
|
|
870
|
-
const pathsInterface = await this.generatePathsInterface(endpointInfos);
|
|
871
|
-
return this.buildModule({
|
|
872
|
-
title,
|
|
873
|
-
version,
|
|
874
|
-
description,
|
|
875
|
-
securitySchemes,
|
|
876
|
-
endpointAuth,
|
|
877
|
-
schemaInterfaces,
|
|
878
|
-
pathsInterface
|
|
879
|
-
});
|
|
880
|
-
}
|
|
881
|
-
async extractEndpointInfos(endpoints) {
|
|
882
|
-
return endpoints.map((ep) => {
|
|
883
|
-
const route = ep.route.replace(/:(\w+)/g, "{$1}");
|
|
884
|
-
const method = ep.method.toUpperCase();
|
|
885
|
-
const securityScheme = ep.authorizer?.securityScheme;
|
|
886
|
-
return {
|
|
887
|
-
endpoint: `${method} ${route}`,
|
|
888
|
-
route,
|
|
889
|
-
method,
|
|
890
|
-
authorizerName: ep.authorizer?.name ?? null,
|
|
891
|
-
authorizerType: ep.authorizer?.type ?? null,
|
|
892
|
-
securityScheme: securityScheme ?? null,
|
|
893
|
-
input: ep.input,
|
|
894
|
-
output: ep.outputSchema,
|
|
895
|
-
description: ep.description,
|
|
896
|
-
tags: ep.tags,
|
|
897
|
-
operationId: ep.operationId,
|
|
898
|
-
responseType: ep.responseType ?? "application/json"
|
|
899
|
-
};
|
|
900
|
-
});
|
|
901
|
-
}
|
|
902
|
-
collectSecuritySchemes(endpointInfos) {
|
|
903
|
-
const schemes = /* @__PURE__ */ new Map();
|
|
904
|
-
for (const info of endpointInfos) if (info.authorizerName && !schemes.has(info.authorizerName)) {
|
|
905
|
-
const scheme = info.securityScheme ?? (info.authorizerType ? this.mapAuthorizerToSecurityScheme(info.authorizerType, info.authorizerName) : null);
|
|
906
|
-
if (scheme) schemes.set(info.authorizerName, {
|
|
907
|
-
name: info.authorizerName,
|
|
908
|
-
type: scheme.type,
|
|
909
|
-
scheme
|
|
910
|
-
});
|
|
911
|
-
}
|
|
912
|
-
return Array.from(schemes.values());
|
|
913
|
-
}
|
|
914
|
-
mapAuthorizerToSecurityScheme(type, _name) {
|
|
915
|
-
switch (type.toLowerCase()) {
|
|
916
|
-
case "jwt":
|
|
917
|
-
case "bearer": return {
|
|
918
|
-
type: "http",
|
|
919
|
-
scheme: "bearer",
|
|
920
|
-
bearerFormat: "JWT"
|
|
921
|
-
};
|
|
922
|
-
case "iam":
|
|
923
|
-
case "aws-sigv4":
|
|
924
|
-
case "sigv4": return {
|
|
925
|
-
type: "apiKey",
|
|
926
|
-
in: "header",
|
|
927
|
-
name: "Authorization",
|
|
928
|
-
"x-amazon-apigateway-authtype": "awsSigv4"
|
|
929
|
-
};
|
|
930
|
-
case "apikey":
|
|
931
|
-
case "api-key": return {
|
|
932
|
-
type: "apiKey",
|
|
933
|
-
in: "header",
|
|
934
|
-
name: "X-API-Key"
|
|
935
|
-
};
|
|
936
|
-
case "oauth2": return {
|
|
937
|
-
type: "oauth2",
|
|
938
|
-
flows: {}
|
|
939
|
-
};
|
|
940
|
-
case "oidc":
|
|
941
|
-
case "openidconnect": return {
|
|
942
|
-
type: "openIdConnect",
|
|
943
|
-
openIdConnectUrl: ""
|
|
944
|
-
};
|
|
945
|
-
default: return {
|
|
946
|
-
type: "http",
|
|
947
|
-
scheme: "bearer"
|
|
948
|
-
};
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
buildEndpointAuthMap(endpointInfos) {
|
|
952
|
-
const authMap = {};
|
|
953
|
-
for (const info of endpointInfos) authMap[info.endpoint] = info.authorizerName;
|
|
954
|
-
return authMap;
|
|
955
|
-
}
|
|
956
|
-
async generateSchemaInterfaces(endpointInfos) {
|
|
957
|
-
const interfaces = [];
|
|
958
|
-
const generatedNames = /* @__PURE__ */ new Set();
|
|
959
|
-
const collectedDefs = /* @__PURE__ */ new Map();
|
|
960
|
-
for (const info of endpointInfos) {
|
|
961
|
-
const baseName = this.getSchemaBaseName(info);
|
|
962
|
-
if (info.input?.body) {
|
|
963
|
-
const name = await this.getSchemaName(info.input.body, `${baseName}Input`);
|
|
964
|
-
if (!generatedNames.has(name)) {
|
|
965
|
-
const schema = await this.schemaToInterfaceWithDefs(info.input.body, name, collectedDefs);
|
|
966
|
-
if (schema) {
|
|
967
|
-
interfaces.push(schema);
|
|
968
|
-
generatedNames.add(name);
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
if (info.input?.params) {
|
|
973
|
-
const name = await this.getSchemaName(info.input.params, `${baseName}Params`);
|
|
974
|
-
if (!generatedNames.has(name)) {
|
|
975
|
-
const schema = await this.schemaToInterfaceWithDefs(info.input.params, name, collectedDefs);
|
|
976
|
-
if (schema) {
|
|
977
|
-
interfaces.push(schema);
|
|
978
|
-
generatedNames.add(name);
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
if (info.input?.query) {
|
|
983
|
-
const name = await this.getSchemaName(info.input.query, `${baseName}Query`);
|
|
984
|
-
if (!generatedNames.has(name)) {
|
|
985
|
-
const schema = await this.schemaToInterfaceWithDefs(info.input.query, name, collectedDefs);
|
|
986
|
-
if (schema) {
|
|
987
|
-
interfaces.push(schema);
|
|
988
|
-
generatedNames.add(name);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
if (info.output) {
|
|
993
|
-
const name = await this.getSchemaName(info.output, `${baseName}Output`);
|
|
994
|
-
if (!generatedNames.has(name)) {
|
|
995
|
-
const schema = await this.schemaToInterfaceWithDefs(info.output, name, collectedDefs);
|
|
996
|
-
if (schema) {
|
|
997
|
-
interfaces.push(schema);
|
|
998
|
-
generatedNames.add(name);
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
const registrySchemas = await (0, _geekmidas_schema_conversion.getRegisteredZodJsonSchemas)();
|
|
1004
|
-
for (const [rawName, rawSchema] of Object.entries(registrySchemas)) {
|
|
1005
|
-
const defName = this.pascalCase(rawName);
|
|
1006
|
-
if (collectedDefs.has(defName) || generatedNames.has(defName)) continue;
|
|
1007
|
-
const { id: _id, $defs: _defs, ...rest } = rawSchema;
|
|
1008
|
-
collectedDefs.set(defName, rest);
|
|
1009
|
-
}
|
|
1010
|
-
for (const [defName, defSchema] of collectedDefs) if (!generatedNames.has(defName)) {
|
|
1011
|
-
const interfaceStr = this.jsonSchemaToInterface(defSchema, defName);
|
|
1012
|
-
interfaces.push(interfaceStr);
|
|
1013
|
-
generatedNames.add(defName);
|
|
1014
|
-
}
|
|
1015
|
-
return interfaces.join("\n\n");
|
|
1016
|
-
}
|
|
1017
|
-
/**
|
|
1018
|
-
* Get the name for a schema, using metadata `id` if available,
|
|
1019
|
-
* otherwise falling back to the provided default name.
|
|
1020
|
-
*/
|
|
1021
|
-
async getSchemaName(schema, defaultName) {
|
|
1022
|
-
try {
|
|
1023
|
-
const metadata = await (0, _geekmidas_schema_conversion.getSchemaMetadata)(schema);
|
|
1024
|
-
if (metadata?.id) return this.pascalCase(metadata.id);
|
|
1025
|
-
} catch {}
|
|
1026
|
-
return defaultName;
|
|
1027
|
-
}
|
|
1028
|
-
getSchemaBaseName(info) {
|
|
1029
|
-
if (info.operationId) return this.pascalCase(info.operationId);
|
|
1030
|
-
const routeParts = info.route.replace(/[{}]/g, "").split("/").filter(Boolean).map((part) => this.pascalCase(part));
|
|
1031
|
-
return `${this.pascalCase(info.method.toLowerCase())}${routeParts.join("")}`;
|
|
1032
|
-
}
|
|
1033
|
-
pascalCase(str) {
|
|
1034
|
-
return str.replace(/[-_](.)/g, (_, c) => c.toUpperCase()).replace(/^./, (c) => c.toUpperCase());
|
|
1035
|
-
}
|
|
1036
|
-
/**
|
|
1037
|
-
* Convert schema to interface while collecting $defs for nested schemas
|
|
1038
|
-
* with .meta({ id: 'X' }).
|
|
1039
|
-
*/
|
|
1040
|
-
async schemaToInterfaceWithDefs(schema, name, collectedDefs) {
|
|
1041
|
-
try {
|
|
1042
|
-
const vendor = schema["~standard"]?.vendor;
|
|
1043
|
-
if (!vendor || !(vendor in _geekmidas_schema_conversion.StandardSchemaJsonSchema)) return unknownType(name);
|
|
1044
|
-
const toJsonSchema = _geekmidas_schema_conversion.StandardSchemaJsonSchema[vendor];
|
|
1045
|
-
const jsonSchema = await toJsonSchema(schema);
|
|
1046
|
-
if (!jsonSchema) return unknownType(name);
|
|
1047
|
-
if (jsonSchema.$defs && typeof jsonSchema.$defs === "object") {
|
|
1048
|
-
for (const [defName, defSchema] of Object.entries(jsonSchema.$defs)) if (!collectedDefs.has(defName)) {
|
|
1049
|
-
const { id, ...schemaWithoutId } = defSchema;
|
|
1050
|
-
collectedDefs.set(defName, schemaWithoutId);
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
const { $defs, ...schemaWithoutDefs } = jsonSchema;
|
|
1054
|
-
const ref = schemaWithoutDefs.$ref;
|
|
1055
|
-
if (ref && Object.keys(schemaWithoutDefs).every((k) => k === "$ref" || k === "$schema")) {
|
|
1056
|
-
const target = ref.split("/").pop();
|
|
1057
|
-
return target === name ? null : `export type ${name} = ${target};`;
|
|
1058
|
-
}
|
|
1059
|
-
return this.jsonSchemaToInterface(schemaWithoutDefs, name);
|
|
1060
|
-
} catch {
|
|
1061
|
-
return unknownType(name);
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
jsonSchemaToInterface(schema, name) {
|
|
1065
|
-
if (schema.type !== "object" || !schema.properties) return `export type ${name} = ${this.jsonSchemaTypeToTs(schema)};`;
|
|
1066
|
-
const props = [];
|
|
1067
|
-
const required = new Set(schema.required || []);
|
|
1068
|
-
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
1069
|
-
const isRequired = required.has(propName);
|
|
1070
|
-
const typeStr = this.jsonSchemaTypeToTs(propSchema);
|
|
1071
|
-
const optionalMark = isRequired ? "" : "?";
|
|
1072
|
-
props.push(` ${propName}${optionalMark}: ${typeStr};`);
|
|
1073
|
-
}
|
|
1074
|
-
return `export interface ${name} {\n${props.join("\n")}\n}`;
|
|
1075
|
-
}
|
|
1076
|
-
jsonSchemaTypeToTs(schema) {
|
|
1077
|
-
if (!schema) return "unknown";
|
|
1078
|
-
if (schema.$ref) return schema.$ref.split("/").pop() || "unknown";
|
|
1079
|
-
if (schema.const !== void 0) return typeof schema.const === "string" ? `'${schema.const}'` : String(schema.const);
|
|
1080
|
-
if (schema.anyOf) return schema.anyOf.map((s) => this.jsonSchemaTypeToTs(s)).join(" | ");
|
|
1081
|
-
if (schema.oneOf) return schema.oneOf.map((s) => this.jsonSchemaTypeToTs(s)).join(" | ");
|
|
1082
|
-
if (schema.allOf) return schema.allOf.map((s) => this.jsonSchemaTypeToTs(s)).join(" & ");
|
|
1083
|
-
if (Array.isArray(schema.type)) return schema.type.map((type) => this.jsonSchemaTypeToTs({
|
|
1084
|
-
...schema,
|
|
1085
|
-
type
|
|
1086
|
-
})).join(" | ");
|
|
1087
|
-
switch (schema.type) {
|
|
1088
|
-
case "string":
|
|
1089
|
-
if (schema.enum) return schema.enum.map((e) => `'${e}'`).join(" | ");
|
|
1090
|
-
return "string";
|
|
1091
|
-
case "number":
|
|
1092
|
-
case "integer": return "number";
|
|
1093
|
-
case "boolean": return "boolean";
|
|
1094
|
-
case "null": return "null";
|
|
1095
|
-
case "array":
|
|
1096
|
-
if (schema.items) return `Array<${this.jsonSchemaTypeToTs(schema.items)}>`;
|
|
1097
|
-
return "Array<unknown>";
|
|
1098
|
-
case "object":
|
|
1099
|
-
if (schema.properties) {
|
|
1100
|
-
const props = [];
|
|
1101
|
-
const required = new Set(schema.required || []);
|
|
1102
|
-
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
1103
|
-
const isRequired = required.has(propName);
|
|
1104
|
-
const typeStr = this.jsonSchemaTypeToTs(propSchema);
|
|
1105
|
-
const optionalMark = isRequired ? "" : "?";
|
|
1106
|
-
props.push(`${propName}${optionalMark}: ${typeStr}`);
|
|
1107
|
-
}
|
|
1108
|
-
return `{ ${props.join("; ")} }`;
|
|
1109
|
-
}
|
|
1110
|
-
if (schema.additionalProperties) return `Record<string, ${this.jsonSchemaTypeToTs(schema.additionalProperties)}>`;
|
|
1111
|
-
return "Record<string, unknown>";
|
|
1112
|
-
default: return "unknown";
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
async generatePathsInterface(endpointInfos) {
|
|
1116
|
-
const pathGroups = /* @__PURE__ */ new Map();
|
|
1117
|
-
for (const info of endpointInfos) {
|
|
1118
|
-
const existing = pathGroups.get(info.route) || [];
|
|
1119
|
-
existing.push(info);
|
|
1120
|
-
pathGroups.set(info.route, existing);
|
|
1121
|
-
}
|
|
1122
|
-
const pathEntries = [];
|
|
1123
|
-
for (const [route, infos] of pathGroups) {
|
|
1124
|
-
const methodEntries = [];
|
|
1125
|
-
for (const info of infos) {
|
|
1126
|
-
const methodDef = await this.generateMethodDefinition(info);
|
|
1127
|
-
methodEntries.push(` ${info.method.toLowerCase()}: ${methodDef};`);
|
|
1128
|
-
}
|
|
1129
|
-
const firstWithParams = infos.find((i) => i.input?.params);
|
|
1130
|
-
let paramsEntry = "";
|
|
1131
|
-
if (firstWithParams?.input?.params) paramsEntry = `\n parameters: {\n path: ${await this.getSchemaName(firstWithParams.input.params, `${this.getSchemaBaseName(firstWithParams)}Params`)};\n };`;
|
|
1132
|
-
pathEntries.push(` '${route}': {${paramsEntry}\n${methodEntries.join("\n")}\n };`);
|
|
1133
|
-
}
|
|
1134
|
-
return `export interface paths {\n${pathEntries.join("\n")}\n}`;
|
|
1135
|
-
}
|
|
1136
|
-
async generateMethodDefinition(info) {
|
|
1137
|
-
const parts = [];
|
|
1138
|
-
const baseName = this.getSchemaBaseName(info);
|
|
1139
|
-
if (info.input?.body) {
|
|
1140
|
-
const bodyName = await this.getSchemaName(info.input.body, `${baseName}Input`);
|
|
1141
|
-
parts.push(`requestBody: {
|
|
1142
|
-
content: {
|
|
1143
|
-
'application/json': ${bodyName};
|
|
1144
|
-
};
|
|
1145
|
-
}`);
|
|
1146
|
-
}
|
|
1147
|
-
if (info.input?.query) {
|
|
1148
|
-
const queryName = await this.getSchemaName(info.input.query, `${baseName}Query`);
|
|
1149
|
-
parts.push(`parameters: {
|
|
1150
|
-
query: ${queryName};
|
|
1151
|
-
}`);
|
|
1152
|
-
}
|
|
1153
|
-
const outputName = info.output ? await this.getSchemaName(info.output, `${baseName}Output`) : "unknown";
|
|
1154
|
-
parts.push(`responses: {
|
|
1155
|
-
200: {
|
|
1156
|
-
content: {
|
|
1157
|
-
'${info.responseType}': ${outputName};
|
|
1158
|
-
};
|
|
1159
|
-
};
|
|
1160
|
-
}`);
|
|
1161
|
-
return `{\n ${parts.join(";\n ")};\n }`;
|
|
1162
|
-
}
|
|
1163
|
-
buildModule(params) {
|
|
1164
|
-
const { title, version, description, securitySchemes, endpointAuth, schemaInterfaces, pathsInterface } = params;
|
|
1165
|
-
const securitySchemesObj = securitySchemes.reduce((acc, s) => {
|
|
1166
|
-
acc[s.name] = s.scheme;
|
|
1167
|
-
return acc;
|
|
1168
|
-
}, {});
|
|
1169
|
-
const schemeNames = securitySchemes.map((s) => `'${s.name}'`).join(" | ");
|
|
1170
|
-
const createApiSection = schemeNames.length > 0 ? `
|
|
1171
|
-
// ============================================================
|
|
1172
|
-
// API Client Factory
|
|
1173
|
-
// ============================================================
|
|
1174
|
-
|
|
1175
|
-
import {
|
|
1176
|
-
createAuthAwareFetcher,
|
|
1177
|
-
type AuthStrategy,
|
|
1178
|
-
} from '@geekmidas/client/auth-fetcher';
|
|
1179
|
-
import { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';
|
|
1180
|
-
import type { QueryClient } from '@tanstack/react-query';
|
|
1181
|
-
|
|
1182
|
-
/**
|
|
1183
|
-
* Options for creating the API client.
|
|
1184
|
-
*/
|
|
1185
|
-
export interface CreateApiOptions {
|
|
1186
|
-
/** Base URL for all API requests (required) */
|
|
1187
|
-
baseURL: string;
|
|
1188
|
-
/** Auth strategies for each security scheme used in this API */
|
|
1189
|
-
authStrategies: Record<SecuritySchemeId, AuthStrategy>;
|
|
1190
|
-
/** Optional React Query client instance */
|
|
1191
|
-
queryClient?: QueryClient;
|
|
1192
|
-
/** Optional request interceptor */
|
|
1193
|
-
onRequest?: (config: RequestInit) => RequestInit | Promise<RequestInit>;
|
|
1194
|
-
/** The \`fetch\` requests go out on — a test browser's, for one */
|
|
1195
|
-
fetch?: typeof fetch;
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
/**
|
|
1199
|
-
* Create a type-safe API client with authentication and React Query hooks.
|
|
1200
|
-
*
|
|
1201
|
-
* @example
|
|
1202
|
-
* \`\`\`typescript
|
|
1203
|
-
* const api = createApi({
|
|
1204
|
-
* baseURL: 'https://api.example.com',
|
|
1205
|
-
* authStrategies: {
|
|
1206
|
-
* jwt: { type: 'bearer', tokenProvider },
|
|
1207
|
-
* },
|
|
1208
|
-
* });
|
|
1209
|
-
*
|
|
1210
|
-
* // Imperative fetch
|
|
1211
|
-
* const user = await api('GET /users/{id}', { params: { id: '123' } });
|
|
1212
|
-
*
|
|
1213
|
-
* // React Query hooks
|
|
1214
|
-
* const { data } = api.useQuery('GET /users/{id}', { params: { id: '123' } });
|
|
1215
|
-
* const mutation = api.useMutation('POST /users');
|
|
1216
|
-
* \`\`\`
|
|
1217
|
-
*/
|
|
1218
|
-
export function createApi(options: CreateApiOptions) {
|
|
1219
|
-
const fetcher = createAuthAwareFetcher<paths, typeof endpointAuth, typeof securitySchemes>({
|
|
1220
|
-
baseURL: options.baseURL,
|
|
1221
|
-
endpointAuth,
|
|
1222
|
-
securitySchemes,
|
|
1223
|
-
authStrategies: options.authStrategies,
|
|
1224
|
-
onRequest: options.onRequest,
|
|
1225
|
-
...(options.fetch ? { fetch: options.fetch } : {}),
|
|
1226
|
-
});
|
|
1227
|
-
|
|
1228
|
-
const hooks = createEndpointHooks<paths>(fetcher, { queryClient: options.queryClient });
|
|
1229
|
-
|
|
1230
|
-
return Object.assign(fetcher, hooks);
|
|
1231
|
-
}
|
|
1232
|
-
` : `
|
|
1233
|
-
// ============================================================
|
|
1234
|
-
// API Client Factory
|
|
1235
|
-
// ============================================================
|
|
1236
|
-
|
|
1237
|
-
import { createTypedFetcher, type FetcherOptions } from '@geekmidas/client/fetcher';
|
|
1238
|
-
import { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';
|
|
1239
|
-
import type { QueryClient } from '@tanstack/react-query';
|
|
1240
|
-
|
|
1241
|
-
/**
|
|
1242
|
-
* Options for creating the API client.
|
|
1243
|
-
*/
|
|
1244
|
-
export interface CreateApiOptions extends Omit<FetcherOptions, 'baseURL'> {
|
|
1245
|
-
/** Base URL for all API requests (required) */
|
|
1246
|
-
baseURL: string;
|
|
1247
|
-
/** Optional React Query client instance */
|
|
1248
|
-
queryClient?: QueryClient;
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
/**
|
|
1252
|
-
* Create a type-safe API client with React Query hooks.
|
|
1253
|
-
*
|
|
1254
|
-
* @example
|
|
1255
|
-
* \`\`\`typescript
|
|
1256
|
-
* const api = createApi({
|
|
1257
|
-
* baseURL: 'https://api.example.com',
|
|
1258
|
-
* });
|
|
1259
|
-
*
|
|
1260
|
-
* // Imperative fetch
|
|
1261
|
-
* const data = await api('GET /health');
|
|
1262
|
-
*
|
|
1263
|
-
* // React Query hooks
|
|
1264
|
-
* const { data } = api.useQuery('GET /health');
|
|
1265
|
-
* \`\`\`
|
|
1266
|
-
*/
|
|
1267
|
-
export function createApi(options: CreateApiOptions) {
|
|
1268
|
-
const { queryClient, ...fetcherOptions } = options;
|
|
1269
|
-
const request = createTypedFetcher<paths>(fetcherOptions);
|
|
1270
|
-
|
|
1271
|
-
const hooks = createEndpointHooks<paths>(request, { queryClient });
|
|
1272
|
-
|
|
1273
|
-
return Object.assign(request, hooks);
|
|
1274
|
-
}
|
|
1275
|
-
`;
|
|
1276
|
-
return `// Auto-generated by @geekmidas/cli - DO NOT EDIT
|
|
1277
|
-
|
|
1278
|
-
// ============================================================
|
|
1279
|
-
// Security Scheme Type
|
|
1280
|
-
// ============================================================
|
|
1281
|
-
|
|
1282
|
-
interface SecuritySchemeObject {
|
|
1283
|
-
type: 'apiKey' | 'http' | 'mutualTLS' | 'oauth2' | 'openIdConnect';
|
|
1284
|
-
description?: string;
|
|
1285
|
-
name?: string;
|
|
1286
|
-
in?: 'query' | 'header' | 'cookie';
|
|
1287
|
-
scheme?: string;
|
|
1288
|
-
bearerFormat?: string;
|
|
1289
|
-
flows?: Record<string, unknown>;
|
|
1290
|
-
openIdConnectUrl?: string;
|
|
1291
|
-
[key: string]: unknown;
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
// ============================================================
|
|
1295
|
-
// API Info
|
|
1296
|
-
// ============================================================
|
|
1297
|
-
|
|
1298
|
-
export const apiInfo = {
|
|
1299
|
-
title: '${title}',
|
|
1300
|
-
version: '${version}',${description ? `\n description: '${description.replace(/'/g, "\\'")}',` : ""}
|
|
1301
|
-
} as const;
|
|
1302
|
-
|
|
1303
|
-
// ============================================================
|
|
1304
|
-
// Security Schemes
|
|
1305
|
-
// ============================================================
|
|
1306
|
-
|
|
1307
|
-
/**
|
|
1308
|
-
* Available security schemes for this API.
|
|
1309
|
-
* Maps authorizer names to OpenAPI security scheme definitions.
|
|
1310
|
-
*/
|
|
1311
|
-
export const securitySchemes = ${JSON.stringify(securitySchemesObj, null, 2).replace(/"([a-zA-Z_$][a-zA-Z0-9_$]*)":/g, "$1:")} as const satisfies Record<string, SecuritySchemeObject>;
|
|
1312
|
-
|
|
1313
|
-
export type SecuritySchemeId = ${schemeNames || "never"};
|
|
1314
|
-
|
|
1315
|
-
// ============================================================
|
|
1316
|
-
// Endpoint Authentication Map
|
|
1317
|
-
// ============================================================
|
|
1318
|
-
|
|
1319
|
-
/**
|
|
1320
|
-
* Runtime map of endpoints to their required authentication scheme.
|
|
1321
|
-
* \`null\` indicates a public endpoint (no auth required).
|
|
1322
|
-
*/
|
|
1323
|
-
export const endpointAuth = ${JSON.stringify(endpointAuth, null, 2).replace(/"([^"]+)":/g, "'$1':")} as const satisfies Record<string, SecuritySchemeId | null>;
|
|
1324
|
-
|
|
1325
|
-
export type EndpointString = keyof typeof endpointAuth;
|
|
1326
|
-
|
|
1327
|
-
export type AuthenticatedEndpoint = {
|
|
1328
|
-
[K in EndpointString]: typeof endpointAuth[K] extends null ? never : K;
|
|
1329
|
-
}[EndpointString];
|
|
1330
|
-
|
|
1331
|
-
export type PublicEndpoint = {
|
|
1332
|
-
[K in EndpointString]: typeof endpointAuth[K] extends null ? K : never;
|
|
1333
|
-
}[EndpointString];
|
|
1334
|
-
|
|
1335
|
-
// ============================================================
|
|
1336
|
-
// Schema Definitions
|
|
1337
|
-
// ============================================================
|
|
1338
|
-
|
|
1339
|
-
${schemaInterfaces}
|
|
1340
|
-
|
|
1341
|
-
// ============================================================
|
|
1342
|
-
// OpenAPI Paths
|
|
1343
|
-
// ============================================================
|
|
1344
|
-
|
|
1345
|
-
${pathsInterface}
|
|
1346
|
-
${createApiSection}
|
|
1347
|
-
`;
|
|
1348
|
-
}
|
|
1349
|
-
};
|
|
1350
|
-
/**
|
|
1351
|
-
* The declaration for a schema that cannot be converted — a vendor with no
|
|
1352
|
-
* converter, or one that fails. The paths interface names it either way, so
|
|
1353
|
-
* leaving it undeclared made the generated client fail to compile.
|
|
1354
|
-
*/
|
|
1355
|
-
function unknownType(name) {
|
|
1356
|
-
return `export type ${name} = unknown;`;
|
|
1357
|
-
}
|
|
1358
|
-
//#endregion
|
|
1359
853
|
Object.defineProperty(exports, "EndpointGenerator", {
|
|
1360
854
|
enumerable: true,
|
|
1361
855
|
get: function() {
|
|
1362
856
|
return EndpointGenerator;
|
|
1363
857
|
}
|
|
1364
858
|
});
|
|
1365
|
-
Object.defineProperty(exports, "
|
|
859
|
+
Object.defineProperty(exports, "generateTelemetryModule", {
|
|
1366
860
|
enumerable: true,
|
|
1367
861
|
get: function() {
|
|
1368
|
-
return
|
|
862
|
+
return generateTelemetryModule;
|
|
1369
863
|
}
|
|
1370
864
|
});
|
|
1371
865
|
Object.defineProperty(exports, "runtimeFor", {
|
|
@@ -1381,4 +875,4 @@ Object.defineProperty(exports, "telemetryFor", {
|
|
|
1381
875
|
}
|
|
1382
876
|
});
|
|
1383
877
|
|
|
1384
|
-
//# sourceMappingURL=
|
|
878
|
+
//# sourceMappingURL=EndpointGenerator-CP6sBvQV.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EndpointGenerator-CP6sBvQV.cjs","names":["resolve","dirname","join","existsSync","realpathSync","basename","relative","ConstructGenerator","Endpoint","join","mkdir","dirname","writeFile"],"sources":["../src/generators/telemetry.ts","../src/generators/EndpointGenerator.ts"],"sourcesContent":["import { existsSync, realpathSync } from 'node:fs';\nimport { basename, dirname, join, resolve } from 'node:path';\n\n/**\n * What a production entry needs to start OpenTelemetry, decided at build time.\n *\n * Carried on the build context so the generator prints values rather than\n * working them out: which service this is, which product it belongs to, and\n * whether the packages to export with were there to build against.\n */\nexport interface TelemetryContext {\n\t/** `service.name` — the surface the server serves, or the app's directory. */\n\tserviceName: string;\n\t/** `service.namespace` — the workspace the app is part of. */\n\tserviceNamespace?: string;\n\t/**\n\t * Whether `@geekmidas/telescope/instrumentation` and the OpenTelemetry\n\t * packages it imports resolve from the app.\n\t *\n\t * They are optional peers, so an app may not have them. An entry built\n\t * without them never imports them: the bundler would otherwise fail on the\n\t * first one it cannot find, and an app that never asked for telemetry\n\t * would stop building.\n\t */\n\tavailable: boolean;\n}\n\n/**\n * Every package `@geekmidas/telescope/instrumentation` loads at runtime.\n *\n * All of them, rather than one as a proxy: the production bundle follows each\n * import, and a single missing one fails the build rather than the telemetry.\n */\nexport const TELEMETRY_PACKAGES = [\n\t'@opentelemetry/api',\n\t'@opentelemetry/auto-instrumentations-node',\n\t'@opentelemetry/exporter-logs-otlp-http',\n\t'@opentelemetry/exporter-trace-otlp-http',\n\t'@opentelemetry/instrumentation-pino',\n\t'@opentelemetry/resources',\n\t'@opentelemetry/sdk-logs',\n\t'@opentelemetry/sdk-node',\n\t'@opentelemetry/sdk-trace-base',\n\t'@opentelemetry/sdk-trace-node',\n\t'@opentelemetry/semantic-conventions',\n] as const;\n\n/**\n * The directory of `name` as `node_modules` lookup finds it from `fromDir`,\n * symlinks resolved — or undefined.\n *\n * The directory walk alone, not `require.resolve`: that also searches\n * `NODE_PATH`, which a package manager's bin shim sets to its own store, so a\n * `gkm build` run through one would find packages the app never installed —\n * and the bundler, which does not look there, would then fail on them.\n */\nfunction packageDir(fromDir: string, name: string): string | undefined {\n\tfor (let dir = resolve(fromDir); ; dir = dirname(dir)) {\n\t\tconst candidate = join(dir, 'node_modules', name);\n\t\tif (existsSync(join(candidate, 'package.json'))) {\n\t\t\treturn realpathSync(candidate);\n\t\t}\n\t\tif (dirname(dir) === dir) return undefined;\n\t}\n}\n\n/**\n * Whether the telemetry packages resolve the way the bundler will resolve them:\n * telescope from the app, and the OpenTelemetry packages from telescope —\n * where a peer is linked under pnpm, which need not be where the app sees it.\n */\nexport function telemetryResolvesFrom(appRoot: string): boolean {\n\tconst telescope = packageDir(appRoot, '@geekmidas/telescope');\n\tif (!telescope) return false;\n\n\treturn TELEMETRY_PACKAGES.every((pkg) => packageDir(telescope, pkg));\n}\n\n/** The telemetry half of a build context. */\nexport function telemetryFor(options: {\n\tappRoot: string;\n\tsurfaceId?: string;\n\tworkspaceName?: string;\n}): TelemetryContext {\n\treturn {\n\t\tserviceName: options.surfaceId ?? basename(options.appRoot),\n\t\t...(options.workspaceName && { serviceNamespace: options.workspaceName }),\n\t\tavailable: telemetryResolvesFrom(options.appRoot),\n\t};\n}\n\nconst HEADER = `/**\n * Generated by 'gkm build --production'.\n *\n * Starts OpenTelemetry when OTEL_EXPORTER_OTLP_ENDPOINT names a collector, and\n * does nothing otherwise: without it, the telemetry packages are not loaded.\n * The standard OTEL_* variables (OTEL_TRACES_SAMPLER, OTEL_TRACES_SAMPLER_ARG,\n * OTEL_EXPORTER_OTLP_HEADERS, OTEL_RESOURCE_ATTRIBUTES, OTEL_SERVICE_NAME)\n * configure the rest.\n */\n\nimport type { MiddlewareHandler } from 'hono';\n\n/** Requests whose spans would only be noise: health checks, by default. */\nexport interface StartTelemetryOptions {\n ignorePaths?: string[];\n}\n\n/** Telemetry was asked for and cannot start. The server runs without it. */\nexport class TelemetryUnavailable extends Error {\n constructor(readonly reason: string, options?: { cause?: unknown }) {\n super(\n \\`OTEL_EXPORTER_OTLP_ENDPOINT is set, but \\${reason} This server exports no traces or logs until that is fixed.\\`,\n options,\n );\n this.name = 'TelemetryUnavailable';\n }\n}\n`;\n\n/**\n * The entry's `telemetry.ts`: a `startTelemetry()` the server awaits before it\n * imports the app, so the libraries the app loads are instrumented. It returns\n * the Hono middleware that traces each request, or undefined when telemetry is\n * off — the server mounts it ahead of every route.\n *\n * Failing to start telemetry warns (a named `TelemetryUnavailable`, through\n * `process.emitWarning`) and never stops the server: an outage in observability\n * is not a reason to have an outage in the service.\n */\nexport function generateTelemetryModule(\n\ttelemetry: TelemetryContext | undefined,\n): string {\n\tif (!telemetry?.available) {\n\t\treturn `${HEADER}\nexport async function startTelemetry(\n _options: StartTelemetryOptions = {},\n): Promise<MiddlewareHandler | undefined> {\n if (!process.env.OTEL_EXPORTER_OTLP_ENDPOINT) return undefined;\n\n process.emitWarning(\n new TelemetryUnavailable(\n 'this server was built without @geekmidas/telescope and the @opentelemetry packages it needs. Add them to the app and rebuild.',\n ),\n );\n return undefined;\n}\n`;\n\t}\n\n\tconst options = [\n\t\t`serviceName: ${JSON.stringify(telemetry.serviceName)},`,\n\t\t...(telemetry.serviceNamespace\n\t\t\t? [`serviceNamespace: ${JSON.stringify(telemetry.serviceNamespace)},`]\n\t\t\t: []),\n\t\t// The stage the deploy set, so one service's stages stay apart.\n\t\t'deploymentEnvironment: process.env.STAGE,',\n\t\t// The exporters read OTEL_EXPORTER_OTLP_* themselves, and the SDK reads\n\t\t// OTEL_TRACES_SAMPLER — so no endpoint and no ratio are printed here.\n\t\t// The server's own shutdown decides when the process exits.\n\t\t'handleSignals: false,',\n\t\t// Neither is left to module-load hooks, which see nothing inside a\n\t\t// bundle: @geekmidas/logger emits each record through the logs API\n\t\t// itself, and the middleware returned below opens each request's span.\n\t\t// Hooked as well where they can be, every record and request would be\n\t\t// sent twice.\n\t\t'instrumentPino: false,',\n\t\t'incomingHttpSpans: false,',\n\t];\n\n\treturn `${HEADER}\nexport async function startTelemetry(\n options: StartTelemetryOptions = {},\n): Promise<MiddlewareHandler | undefined> {\n if (!process.env.OTEL_EXPORTER_OTLP_ENDPOINT) return undefined;\n\n let instrumentation;\n try {\n instrumentation = await import('@geekmidas/telescope/instrumentation');\n } catch (cause) {\n process.emitWarning(\n new TelemetryUnavailable(\n '@geekmidas/telescope/instrumentation or an @opentelemetry package it needs could not be loaded. Install them in the app and rebuild.',\n { cause },\n ),\n );\n return undefined;\n }\n\n instrumentation.setupTelemetry({\n ${options.join('\\n ')}\n });\n\n // Flush what is buffered when the process is told to stop. When nothing\n // else handles the signal, re-raise it once flushed, so the process still\n // stops the way it would have without this listener.\n for (const signal of ['SIGTERM', 'SIGINT'] as const) {\n process.once(signal, () => {\n void instrumentation.flushTelemetry().finally(() => {\n if (process.listenerCount(signal) === 0) process.kill(process.pid, signal);\n });\n });\n }\n\n // A SERVER span per request, opened by the app itself rather than by\n // hooking node:http: \\`GET /users/:id\\`, continuing an incoming traceparent,\n // with the handler — its logs, fetches and queries — running inside it.\n return instrumentation.honoTelemetryMiddleware({\n ignorePaths: options.ignorePaths,\n });\n}\n`;\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { Endpoint } from '@geekmidas/constructs/endpoints';\nimport type { BuildContext } from '../build/types';\nimport type { RouteInfo } from '../types';\nimport type { StorageDrivers } from './drivers';\nimport {\n\tConstructGenerator,\n\ttype GeneratedConstruct,\n\ttype GeneratorOptions,\n} from './Generator';\nimport { generateTelemetryModule } from './telemetry';\n\n/**\n * How a generated entry gets its logger and environment parser.\n *\n * From the surface it serves, when there is one: the surface holds both as\n * *objects*, and discovery already recorded which module declares it, so the\n * entry imports that module and reads them off it.\n *\n * The alternative — the shape this replaces — was naming both in config as\n * module paths, `envParser: './config/env#envParser'`, and printing an import\n * for each. Two strings per app, checked by nothing, describing files the\n * surface was already holding the contents of.\n *\n * Falls back to the configured paths for an app that declares no surface.\n */\nexport function runtimeFor(\n\tcontext: BuildContext,\n\tfromDir: string,\n\t/**\n\t * The construct that owns the thing being generated.\n\t *\n\t * An endpoint's surface, or the worker that handed out the cron factory.\n\t * Omitted for the app entry itself, which belongs to the primary surface.\n\t */\n\towner?: string,\n): { imports: string; bindings: string } {\n\tconst module = owner\n\t\t? (context.owners?.[owner] ?? context.surface?.module)\n\t\t: context.surface?.module;\n\n\tif (module) {\n\t\tconst specifier = importSpecifier(fromDir, module.specifier);\n\n\t\t// Bound to the same names the rest of the entry already uses, so what\n\t\t// changes is where they come from and nothing else.\n\t\treturn {\n\t\t\timports: `import { ${module.exportName} as __surface } from '${specifier}';`,\n\t\t\tbindings: [\n\t\t\t\t'// What this construct was declared with. Objects, not module paths:',\n\t\t\t\t'// the entry imports the owner, so there is nothing to print.',\n\t\t\t\t'const envParser = __surface.envParser;',\n\t\t\t\t'const logger = __surface.logger;',\n\t\t\t].join('\\n'),\n\t\t};\n\t}\n\n\t// No fallback. Every runnable comes from a factory a `RestApi` or a `Worker`\n\t// handed out, and that construct holds the objects — so there is no longer a\n\t// case where the only thing we know about a logger is a module path somebody\n\t// typed into config.\n\tthrow new Error(\n\t\t`No owner to take a logger and an environment parser from${\n\t\t\towner ? ` for \"${owner}\"` : ''\n\t\t}. Build it from a \\`RestApi\\` or a \\`Worker\\`.`,\n\t);\n}\n\n/** A relative specifier ESM will accept: always prefixed, never bare. */\nfunction importSpecifier(fromDir: string, target: string): string {\n\tconst rel = relative(fromDir, target).replace(/\\.ts$/, '.js');\n\n\treturn rel.startsWith('.') ? rel : `./${rel}`;\n}\n\n/**\n * CORS for a surface, derived rather than hand-written.\n *\n * Who may call a surface is already in the graph — every construct that\n * declared an edge to it — and reaches the process as one comma-separated\n * value. An application that maintained this by hand maintained a list that was\n * wrong in two directions at once: it trusted origins nothing was serving, and\n * it could not name hosts a deploy had not chosen yet.\n *\n * Only the parts a graph cannot answer are configurable, and every one has a\n * default: a surface that says nothing about CORS still gets it.\n */\nexport function corsFor(surface: BuildContext['surface']): {\n\timports: string;\n\tsetup: string;\n} {\n\tif (!surface) return { imports: '', setup: '' };\n\n\tconst cors = surface.cors ?? {};\n\tconst allowHeaders = [\n\t\t'content-type',\n\t\t'authorization',\n\t\t...(cors.allowHeaders ?? []),\n\t];\n\n\treturn {\n\t\timports: `import { cors } from 'hono/cors';`,\n\t\tsetup: `\n // Who may call ${surface.id}, read off the graph rather than listed here.\n {\n const { origins } = envParser\n .create((get) => ({\n origins: get('${surface.trustedOriginsKey}')\n .string()\n .default('')\n .transform((value: string) =>\n value.split(',').map((origin) => origin.trim()).filter(Boolean),\n ),\n }))\n .parse();\n\n honoApp.use('*', cors({\n origin: origins,\n allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],\n allowHeaders: ${JSON.stringify(allowHeaders)},${\n\t\t\t\tcors.exposeHeaders\n\t\t\t\t\t? `\\n exposeHeaders: ${JSON.stringify(cors.exposeHeaders)},`\n\t\t\t\t\t: ''\n\t\t\t}\n credentials: ${cors.credentials ?? true},\n maxAge: ${cors.maxAge ?? 86400},\n }));\n }\n`,\n\t};\n}\n\nexport class EndpointGenerator extends ConstructGenerator<\n\tEndpoint<any, any, any, any, any, any, any, any, any, any, any, any>,\n\tRouteInfo[]\n> {\n\tisConstruct(\n\t\tvalue: any,\n\t): value is Endpoint<\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany\n\t> {\n\t\treturn Endpoint.isEndpoint(value);\n\t}\n\n\tasync build(\n\t\tcontext: BuildContext,\n\t\tconstructs: GeneratedConstruct<\n\t\t\tEndpoint<any, any, any, any, any, any, any, any, any, any, any, any>\n\t\t>[],\n\t\toutputDir: string,\n\t\toptions?: GeneratorOptions,\n\t): Promise<RouteInfo[]> {\n\t\tconst target = options?.target ?? 'aws';\n\t\tconst root = options?.root ?? process.cwd();\n\t\tconst enableOpenApi = options?.enableOpenApi || false;\n\t\tconst logger = console;\n\t\tconst routes: RouteInfo[] = [];\n\n\t\tif (constructs.length === 0) {\n\t\t\treturn routes;\n\t\t}\n\n\t\tif (target === 'server') {\n\t\t\t// Generate endpoints.ts and app.ts\n\t\t\tawait this.generateEndpointsFile(outputDir, constructs);\n\t\t\tconst appFile = await this.generateAppFile(outputDir, context);\n\n\t\t\troutes.push({\n\t\t\t\tpath: '*',\n\t\t\t\tmethod: 'ALL',\n\t\t\t\thandler: relative(root, appFile),\n\t\t\t\tauthorizer: 'none',\n\t\t\t});\n\n\t\t\tlogger.log(\n\t\t\t\t`Generated server with ${constructs.length} endpoints${enableOpenApi ? ' (OpenAPI enabled)' : ''}`,\n\t\t\t);\n\n\t\t\treturn routes;\n\t\t}\n\n\t\t// One Lambda per endpoint, behind an HTTP API.\n\t\tconst routesDir = join(outputDir, 'routes');\n\t\tawait mkdir(routesDir, { recursive: true });\n\n\t\tfor (const { key, construct, path } of constructs) {\n\t\t\tconst handlerFile = await this.generateHandlerFile(\n\t\t\t\troutesDir,\n\t\t\t\tpath.relative,\n\t\t\t\tkey,\n\t\t\t\tcontext,\n\t\t\t);\n\n\t\t\tconst routeInfo: RouteInfo = {\n\t\t\t\tpath: construct._path,\n\t\t\t\tmethod: construct.method,\n\t\t\t\thandler: relative(root, handlerFile).replace(/\\.ts$/, '.handler'),\n\t\t\t\ttimeout: construct.timeout,\n\t\t\t\tmemorySize: construct.memorySize,\n\t\t\t\tenvironment: await construct.getEnvironment({\n\t\t\t\t\tmarkOptional: context.markOptional,\n\t\t\t\t}),\n\t\t\t\tdependencies: construct.constructs,\n\t\t\t\tauthorizer: construct.authorizer?.name ?? 'none',\n\t\t\t};\n\n\t\t\troutes.push(routeInfo);\n\t\t\tlogger.log(`Generated handler for ${routeInfo.method} ${routeInfo.path}`);\n\t\t}\n\n\t\treturn routes;\n\t}\n\n\tprivate async generateHandlerFile(\n\t\toutputDir: string,\n\t\tsourceFile: string,\n\t\texportName: string,\n\t\tcontext: BuildContext,\n\t): Promise<string> {\n\t\tconst handlerPath = join(outputDir, `${exportName}.ts`);\n\n\t\tconst relativePath = relative(dirname(handlerPath), sourceFile);\n\t\tconst importPath = relativePath.replace(/\\.ts$/, '.js');\n\n\t\tawait writeFile(\n\t\t\thandlerPath,\n\t\t\tthis.generateAWSApiGatewayV2Handler(\n\t\t\t\timportPath,\n\t\t\t\texportName,\n\t\t\t\tcontext.storageDrivers,\n\t\t\t),\n\t\t);\n\t\treturn handlerPath;\n\t}\n\n\tprivate async generateEndpointsFile(\n\t\toutputDir: string,\n\t\tendpoints: GeneratedConstruct<\n\t\t\tEndpoint<any, any, any, any, any, any, any, any, any, any, any, any>\n\t\t>[],\n\t): Promise<string> {\n\t\tconst endpointsFileName = 'endpoints.ts';\n\t\tconst endpointsPath = join(outputDir, endpointsFileName);\n\n\t\t// Group imports by file\n\t\tconst importsByFile = new Map<string, string[]>();\n\n\t\tfor (const { path, key } of endpoints) {\n\t\t\tconst relativePath = relative(dirname(endpointsPath), path.relative);\n\t\t\tconst importPath = relativePath.replace(/\\.ts$/, '.js');\n\n\t\t\tif (!importsByFile.has(importPath)) {\n\t\t\t\timportsByFile.set(importPath, []);\n\t\t\t}\n\t\t\timportsByFile.get(importPath)?.push(key);\n\t\t}\n\n\t\t// Generate import statements for endpoints\n\t\tconst endpointImports = Array.from(importsByFile.entries())\n\t\t\t.map(\n\t\t\t\t([importPath, exports]) =>\n\t\t\t\t\t`import { ${exports.join(', ')} } from '${importPath}';`,\n\t\t\t)\n\t\t\t.join('\\n');\n\n\t\tconst allExportNames = endpoints.map(({ key }) => key);\n\n\t\t// One registration for every build, production included: each endpoint\n\t\t// is served by HonoEndpoint, which is what `gkm dev` and a feature test\n\t\t// serve it with. Production used to generate its own handlers per\n\t\t// \"tier\", and each was a hand-copy of the adaptor that drifted from it —\n\t\t// a session left undefined, an HttpError answered 500, an auditor\n\t\t// hard-coded to undefined, cookies a handler set never sent.\n\t\tconst content = `import type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport { HonoEndpoint } from '@geekmidas/constructs/hono';\nimport { Endpoint } from '@geekmidas/constructs/endpoints';\nimport { ServiceDiscovery } from '@geekmidas/services';\nimport type { Hono } from 'hono';\n${endpointImports}\n\nexport const endpoints: Endpoint<any, any, any, any, any, any, any, any, any, any, any, any>[] = [\n ${allExportNames.join(',\\n ')}\n];\n\nexport async function setupEndpoints(\n app: Hono,\n envParser: EnvironmentParser<any>,\n logger: Logger,\n enableOpenApi: boolean = true,\n): Promise<void> {\n const serviceDiscovery = ServiceDiscovery.getInstance(envParser);\n\n // Configure OpenAPI options based on enableOpenApi flag\n const openApiOptions: any = enableOpenApi ? {\n docsPath: '/__docs',\n openApiOptions: {\n title: 'API Documentation',\n version: '1.0.0',\n description: 'Generated API documentation'\n }\n } : { docsPath: false };\n\n HonoEndpoint.addRoutes(endpoints, serviceDiscovery, app, openApiOptions);\n\n // Add Swagger UI if OpenAPI is enabled\n if (enableOpenApi) {\n try {\n const { swaggerUI } = await import('@hono/swagger-ui');\n app.get('/__docs/ui', swaggerUI({ url: '/__docs' }));\n } catch {\n // @hono/swagger-ui not installed, skip Swagger UI\n }\n }\n}\n`;\n\n\t\tawait writeFile(endpointsPath, content);\n\n\t\treturn endpointsPath;\n\t}\n\n\tprivate async generateAppFile(\n\t\toutputDir: string,\n\t\tcontext: BuildContext,\n\t): Promise<string> {\n\t\t// Use production generator if in production mode\n\t\tif (context.production?.enabled) {\n\t\t\treturn this.generateProductionAppFile(outputDir, context);\n\t\t}\n\n\t\tconst appFileName = 'app.ts';\n\t\tconst appPath = join(outputDir, appFileName);\n\n\t\t// The logger and the env parser, from the surface that already holds them.\n\t\tconst runtime = runtimeFor(context, dirname(appPath));\n\n\t\t// Generate telescope imports and setup if enabled\n\t\tconst telescopeEnabled = context.telescope?.enabled;\n\t\tconst telescopeWebSocketEnabled = context.telescope?.websocket;\n\t\tconst usesExternalTelescope = !!context.telescope?.telescopePath;\n\n\t\t// Generate imports based on whether telescope is external or inline\n\t\tconst telescopeFromSurface =\n\t\t\t!!context.surface?.module && !usesExternalTelescope;\n\n\t\tlet telescopeImports = '';\n\t\tif (telescopeEnabled) {\n\t\t\tif (telescopeFromSurface) {\n\t\t\t\t// The entry already imports the surface, which may or may not have\n\t\t\t\t// been given a Telescope — an object, so only the running entry can\n\t\t\t\t// tell. The inline one stands in when it was not.\n\t\t\t\ttelescopeImports = `import { Telescope, InMemoryStorage } from '@geekmidas/telescope';\nimport { createApi, createMiddleware } from '@geekmidas/telescope/hono';`;\n\t\t\t} else if (usesExternalTelescope) {\n\t\t\t\tconst relativeTelescopePath = relative(\n\t\t\t\t\tdirname(appPath),\n\t\t\t\t\tcontext.telescope?.telescopePath!,\n\t\t\t\t);\n\t\t\t\ttelescopeImports = `import ${context.telescope?.telescopeImportPattern} from '${relativeTelescopePath}';\nimport { createApi, createMiddleware } from '@geekmidas/telescope/hono';`;\n\t\t\t} else {\n\t\t\t\ttelescopeImports = `import { Telescope, InMemoryStorage } from '@geekmidas/telescope';\nimport { createApi, createMiddleware } from '@geekmidas/telescope/hono';`;\n\t\t\t}\n\t\t}\n\n\t\t// The database's read-only JSON API, from the declared database. The\n\t\t// client comes from the construct, so what it reads is by definition what\n\t\t// the handlers write to.\n\t\tconst databaseApi = context.databaseApi;\n\t\tconst databaseApiImports = databaseApi\n\t\t\t? `import { snifferContext } from '@geekmidas/constructs';\nimport { createIntrospectionHandler } from '@geekmidas/db/introspect';\nimport { ${databaseApi.database.exportName} as __introspectedDb } from '${importSpecifier(dirname(appPath), databaseApi.database.specifier)}';`\n\t\t\t: '';\n\n\t\t// Generate imports for server hooks\n\t\tconst cors = corsFor(context.surface);\n\t\tlet hooksImports = '';\n\t\tlet beforeSetupCall = '';\n\t\tlet afterSetupCall = '';\n\t\tif (context.hooks?.serverHooksPath) {\n\t\t\tconst relativeHooksPath = relative(\n\t\t\t\tdirname(appPath),\n\t\t\t\tcontext.hooks.serverHooksPath,\n\t\t\t);\n\t\t\thooksImports = `import * as serverHooks from '${relativeHooksPath}';`;\n\t\t\tbeforeSetupCall = `\n // Call beforeSetup hook if defined\n if (typeof serverHooks.beforeSetup === 'function') {\n await serverHooks.beforeSetup(honoApp, { envParser, logger });\n }\n`;\n\t\t\tafterSetupCall = `\n // Call afterSetup hook if defined\n if (typeof serverHooks.afterSetup === 'function') {\n await serverHooks.afterSetup(honoApp, { envParser, logger });\n }\n`;\n\t\t}\n\n\t\tconst telescopeWebSocketSetupCode = telescopeWebSocketEnabled\n\t\t\t? `\n // Setup WebSocket for real-time telescope updates\n try {\n const { createNodeWebSocket } = await import('@hono/node-ws');\n const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app: honoApp });\n // Add WebSocket route directly to main app (sub-app routes don't support WS upgrade)\n honoApp.get('${context.telescope?.path}/ws', upgradeWebSocket(() => ({\n onOpen: (_event: Event, ws: any) => {\n telescope.addWsClient(ws);\n },\n onClose: (_event: Event, ws: any) => {\n telescope.removeWsClient(ws);\n },\n onMessage: (event: MessageEvent, ws: any) => {\n try {\n const data = JSON.parse(event.data);\n if (data.type === 'ping') {\n ws.send(JSON.stringify({ type: 'pong' }));\n }\n } catch {\n // Ignore invalid messages\n }\n },\n })));\n // Store injectWebSocket for server entry to call after serve()\n (honoApp as any).__injectWebSocket = injectWebSocket;\n logger.info('Telescope WebSocket enabled');\n } catch (e) {\n logger.warn({ err: e }, 'WebSocket support not available - install @hono/node-ws for real-time updates');\n }\n`\n\t\t\t: '';\n\n\t\t// Generate telescope setup - either use external instance or create inline\n\t\tlet telescopeSetup = '';\n\t\tif (telescopeEnabled) {\n\t\t\tif (telescopeFromSurface || usesExternalTelescope) {\n\t\t\t\t// Use external telescope instance - no need to create one\n\t\t\t\ttelescopeSetup = `\n${telescopeWebSocketSetupCode}\n // Add telescope middleware (before endpoints to capture all requests)\n honoApp.use('*', createMiddleware(telescope));\n\n // Mount Telescope's JSON API\n const telescopeApi = createApi(telescope);\n honoApp.route('${context.telescope?.path}', telescopeApi);\n`;\n\t\t\t} else {\n\t\t\t\t// Create inline telescope instance\n\t\t\t\ttelescopeSetup = `\n // Setup Telescope for debugging/monitoring\n const telescopeStorage = new InMemoryStorage({ maxEntries: ${context.telescope?.maxEntries} });\n const telescope = new Telescope({\n enabled: true,\n path: '${context.telescope?.path}',\n ignorePatterns: ${JSON.stringify(context.telescope?.ignore)},\n recordBody: ${context.telescope?.recordBody},\n storage: telescopeStorage,\n });\n${telescopeWebSocketSetupCode}\n // Add telescope middleware (before endpoints to capture all requests)\n honoApp.use('*', createMiddleware(telescope));\n\n // Mount Telescope's JSON API\n const telescopeApi = createApi(telescope);\n honoApp.route('${context.telescope?.path}', telescopeApi);\n`;\n\t\t\t}\n\t\t}\n\n\t\tconst databaseApiSetup = databaseApi\n\t\t\t? `\n // The database's structure and rows as JSON, read-only, for whatever tool\n // the developer points at it. Dev only: it answers anyone who can reach the\n // port.\n const databaseApi = createIntrospectionHandler({\n db: await __introspectedDb.service.register({ envParser, context: snifferContext }),\n basePath: '${databaseApi.path}',\n });\n honoApp.all('${databaseApi.path}/*', (c) => databaseApi(c.req.raw));\n`\n\t\t\t: '';\n\n\t\tconst content = `/**\n * Generated server application\n *\n * ⚠️ WARNING: This is for LOCAL DEVELOPMENT ONLY\n * The subscriber polling mechanism is not production-ready.\n * For production, use AWS Lambda with SQS/SNS event sources.\n */\nimport { Hono } from 'hono';\nimport type { Hono as HonoType } from 'hono';\nimport { setupEndpoints } from './endpoints.js';\nimport { setupSubscribers } from './subscribers.js';\nimport { setupQueues } from './queues.js';\nimport { setupCrons } from './crons.js';\n${runtime.imports}\n${telescopeImports}\n${databaseApiImports}\n${hooksImports}\n${cors.imports}\n${context.storageDrivers?.imports ?? ''}\n\n${runtime.bindings}\n${\n\ttelescopeEnabled && telescopeFromSurface\n\t\t? `// The surface's own Telescope, or the one \\`telescope\\` config describes.\nconst telescope =\n __surface.telescope ??\n new Telescope({\n enabled: true,\n path: '${context.telescope?.path}',\n ignorePatterns: ${JSON.stringify(context.telescope?.ignore)},\n recordBody: ${context.telescope?.recordBody},\n storage: new InMemoryStorage({ maxEntries: ${context.telescope?.maxEntries} }),\n });`\n\t\t: ''\n}\n\n${\n\tcontext.storageDrivers?.setup\n\t\t? `// Which drivers exist — storage, cache, events — is the entry point's\n// decision: the scheme in each injected URL picks one, so no construct and no\n// application module names a provider.\n${context.storageDrivers.setup}\n`\n\t\t: ''\n}\n${\n\tcontext.surface\n\t\t? `// Who this server is to Postgres: application_name on every connection.\nprocess.env.GKM_APP_NAME ??= ${JSON.stringify(context.surface.id)};\n\n`\n\t\t: ''\n}export interface ServerApp {\n app: HonoType;\n start: (options?: {\n port?: number;\n /** Returns the server, so a shutdown can stop it taking requests. */\n serve: (app: HonoType, port: number) => unknown;\n }) => Promise<void>;\n}\n\n/**\n * Create and configure the Hono application\n *\n * @param app - Optional Hono app instance to configure (creates new one if not provided)\n * @param enableOpenApi - Enable OpenAPI documentation (default: true)\n * @returns Server app with configured Hono app and start function\n *\n * @example\n * // With Bun\n * import { createApp } from './.gkm/server/app.js';\n *\n * const { app, start } = await createApp();\n *\n * await start({\n * port: 3000,\n * serve: (app, port) => {\n * Bun.serve({ port, fetch: app.fetch });\n * }\n * });\n *\n * @example\n * // With Node.js (using @hono/node-server)\n * import { serve } from '@hono/node-server';\n * import { createApp } from './.gkm/server/app.js';\n *\n * const { app, start } = await createApp();\n *\n * await start({\n * port: 3000,\n * serve: (app, port) => {\n * serve({ fetch: app.fetch, port });\n * }\n * });\n */\nexport async function createApp(app?: HonoType, enableOpenApi: boolean = true): Promise<ServerApp> {\n const honoApp = app || new Hono();\n${telescopeSetup}${cors.setup}${beforeSetupCall}${databaseApiSetup}\n // Setup HTTP endpoints\n await setupEndpoints(honoApp, envParser, logger, enableOpenApi);\n${afterSetupCall}\n\n return {\n app: honoApp,\n async start(options) {\n if (!options?.serve) {\n throw new Error(\n 'serve function is required. Pass a serve function for your runtime:\\\\n' +\n ' - Bun: (app, port) => Bun.serve({ port, fetch: app.fetch })\\\\n' +\n ' - Node: (app, port) => serve({ fetch: app.fetch, port })'\n );\n }\n\n const port = options.port ?? 3000;\n\n // Mount pushed subscribers' routes and start polled ones. What comes\n // back subscribes the routes, once the server can answer them.\n const subscribeForPush = await setupSubscribers(honoApp, envParser, logger).catch((error) => {\n logger.error({ err: error }, 'Failed to start subscribers');\n return async (_port: number) => {};\n });\n\n // Start queue workers in background (non-blocking, local development only)\n await setupQueues(envParser, logger).catch((error) => {\n logger.error({ err: error }, 'Failed to start queue workers');\n });\n\n // Schedule this app's crons. Caught like the others: a scheduler that\n // cannot start is not a reason for the HTTP server not to.\n await setupCrons(envParser, logger).catch((error) => {\n logger.error({ err: error }, 'Failed to schedule crons');\n });\n\n logger.info({ port }, 'Starting server');\n\n // Start HTTP server using provided serve function\n await options.serve(honoApp, port);\n\n logger.info({ port }, 'Server started');\n\n await subscribeForPush(port);\n }\n };\n}\n\n// Default export for convenience\nexport default createApp;\n`;\n\n\t\tawait writeFile(appPath, content);\n\n\t\treturn appPath;\n\t}\n\n\t/**\n\t * One Lambda, one endpoint — and no environment import.\n\t *\n\t * The endpoint was built from its surface, so it carries the parser the\n\t * adaptor needs. This used to read\n\t * `import { envParser } from '../../config/env'`, a line the build wrote\n\t * because a generator can print a module specifier and cannot print an\n\t * object. Nothing has to be printed now, which is also why nothing has to be\n\t * named in config.\n\t */\n\tprivate generateAWSApiGatewayV2Handler(\n\t\timportPath: string,\n\t\texportName: string,\n\t\tdrivers?: StorageDrivers,\n\t): string {\n\t\treturn `import { AmazonApiGatewayV2Endpoint } from '@geekmidas/constructs/aws';\nimport { ${exportName} } from '${importPath}';\n${drivers?.imports ?? ''}\n${drivers?.setup ? `\\n// The handler registers the drivers its target needs.\\n${drivers.setup}\\n` : ''}\nconst adapter = new AmazonApiGatewayV2Endpoint(${exportName});\n\nexport const handler = adapter.handler;\n`;\n\t}\n\n\t/**\n\t * Generate a production-optimized app.ts file\n\t * No dev tools (Telescope, Studio, WebSocket), includes health checks and graceful shutdown\n\t */\n\tprivate async generateProductionAppFile(\n\t\toutputDir: string,\n\t\tcontext: BuildContext,\n\t): Promise<string> {\n\t\tconst appFileName = 'app.ts';\n\t\tconst appPath = join(outputDir, appFileName);\n\n\t\t// The app entry belongs to the primary surface, so no owner is named.\n\t\tconst runtime = runtimeFor(context, dirname(appPath));\n\n\t\tconst production = context.production!;\n\t\tconst healthCheckPath = production.healthCheck;\n\t\tconst enableGracefulShutdown = production.gracefulShutdown;\n\t\tconst enableOpenApi = production.openapi;\n\t\tconst includeSubscribers = production.subscribers === 'include';\n\n\t\t// Generate imports for server hooks\n\t\tconst cors = corsFor(context.surface);\n\t\tlet hooksImports = '';\n\t\tlet beforeSetupCall = '';\n\t\tlet afterSetupCall = '';\n\t\tif (context.hooks?.serverHooksPath) {\n\t\t\tconst relativeHooksPath = relative(\n\t\t\t\tdirname(appPath),\n\t\t\t\tcontext.hooks.serverHooksPath,\n\t\t\t);\n\t\t\thooksImports = `import * as serverHooks from '${relativeHooksPath}';`;\n\t\t\tbeforeSetupCall = `\n // Call beforeSetup hook if defined\n if (typeof serverHooks.beforeSetup === 'function') {\n await serverHooks.beforeSetup(honoApp, { envParser, logger });\n }\n`;\n\t\t\tafterSetupCall = `\n // Call afterSetup hook if defined\n if (typeof serverHooks.afterSetup === 'function') {\n await serverHooks.afterSetup(honoApp, { envParser, logger });\n }\n`;\n\t\t}\n\n\t\t// Subscriber + queue setup code (background workers share the same flag)\n\t\tconst subscriberSetup = includeSubscribers\n\t\t\t? `\n // Mount pushed subscribers' routes and start polled ones. What comes\n // back subscribes the routes, once the server can answer them.\n const subscribeForPush = await setupSubscribers(honoApp, envParser, logger).catch((error) => {\n logger.error({ err: error }, 'Failed to start subscribers');\n return async (_port: number) => {};\n });\n\n // Start queue workers in background\n await setupQueues(envParser, logger).catch((error) => {\n logger.error({ err: error }, 'Failed to start queue workers');\n });\n\n // Schedule this app's crons. Caught like the others: a scheduler that\n // cannot start is not a reason for the HTTP server not to.\n await setupCrons(envParser, logger).catch((error) => {\n logger.error({ err: error }, 'Failed to schedule crons');\n });\n`\n\t\t\t: '';\n\n\t\tconst subscriberImport = includeSubscribers\n\t\t\t? `import { setupSubscribers } from './subscribers.js';\nimport { setupQueues } from './queues.js';\nimport { setupCrons } from './crons.js';`\n\t\t\t: '';\n\n\t\t// Graceful shutdown code\n\t\tconst gracefulShutdownCode = enableGracefulShutdown\n\t\t\t? `\n // Graceful shutdown: stop taking requests, let in-flight ones finish, then\n // close what constructs opened (database pools) — so a rolling deploy does\n // not leave the old task holding connections. Bounded by\n // GKM_SHUTDOWN_TIMEOUT_MS, 8s by default: under Docker's 10s stop timeout,\n // so the process exits on its own terms rather than being killed mid-drain.\n let isShuttingDown = false;\n\n const shutdown = async () => {\n if (isShuttingDown) return;\n isShuttingDown = true;\n const deadline = Number(process.env.GKM_SHUTDOWN_TIMEOUT_MS) || 8000;\n logger.info({ deadline }, 'Graceful shutdown initiated');\n setTimeout(() => {\n logger.warn({ deadline }, 'Shutdown deadline reached, exiting');\n process.exit(1);\n }, deadline).unref();\n await new Promise<void>((resolve) => {\n const close = (server as { close?: (done: () => void) => void } | undefined)?.close;\n if (typeof close === 'function') close.call(server, () => resolve());\n else resolve();\n });\n await runShutdownHooks((error) => logger.error({ err: error }, 'Shutdown hook failed'));\n process.exit(0);\n };\n\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n`\n\t\t\t: '';\n\n\t\tconst content = `/**\n * Generated production server application\n *\n * This is a production-optimized build without dev tools.\n * - No Telescope debugging dashboard\n * - No Studio database browser\n * - No WebSocket updates\n * - Includes health checks and graceful shutdown\n */\nimport { Hono } from 'hono';\nimport type { Hono as HonoType } from 'hono';\n${enableGracefulShutdown ? \"import { runShutdownHooks } from '@geekmidas/constructs';\\n\" : ''}import { setupEndpoints } from './endpoints.js';\n${subscriberImport}\n${runtime.imports}\n${runtime.bindings}\n${hooksImports}\n${cors.imports}\n${context.storageDrivers?.imports ?? ''}\n\n${\n\tcontext.storageDrivers?.setup\n\t\t? `// The entry point registers the drivers its target needs.\n${context.storageDrivers.setup}\n`\n\t\t: ''\n}\n${\n\tcontext.surface\n\t\t? `// Who this server is to Postgres: application_name on every connection.\nprocess.env.GKM_APP_NAME ??= ${JSON.stringify(context.surface.id)};\n\n`\n\t\t: ''\n}export interface ServerApp {\n app: HonoType;\n start: (options?: {\n port?: number;\n serve: (app: HonoType, port: number) => void | Promise<void>;\n }) => Promise<void>;\n}\n\n/**\n * Create and configure the production Hono application\n */\nexport async function createApp(app?: HonoType): Promise<ServerApp> {\n const honoApp = app || new Hono();\n\n // Health check endpoint (always first)\n honoApp.get('${healthCheckPath}', (c) => c.json({ status: 'ok', timestamp: Date.now() }));\n honoApp.get('/ready', (c) => c.json({ ready: true }));\n\n // An HttpError a handler or a session callback throws — a 401, a 404 —\n // answers with its own status, as it does under gkm dev. The optimized\n // handlers do not catch, so without this every one of them was a 500.\n // Anything else is a 500 that says nothing about why, and is logged.\n honoApp.onError((error, c) => {\n const http = error as {\n isHttpError?: boolean;\n statusCode?: number;\n statusMessage?: string;\n code?: string;\n details?: unknown;\n };\n if (http.isHttpError === true && typeof http.statusCode === 'number') {\n return c.json(\n {\n name: error.name,\n message: error.message,\n statusCode: http.statusCode,\n statusMessage: http.statusMessage,\n code: http.code,\n details: http.details,\n },\n http.statusCode as 500,\n );\n }\n logger.error({ err: error }, 'Unhandled error');\n return c.json({ message: 'Internal Server Error' }, 500);\n });\n${cors.setup}${beforeSetupCall}\n // Setup HTTP endpoints (OpenAPI: ${enableOpenApi})\n await setupEndpoints(honoApp, envParser, logger, ${enableOpenApi});\n${afterSetupCall}\n return {\n app: honoApp,\n async start(options) {\n if (!options?.serve) {\n throw new Error(\n 'serve function is required. Pass a serve function for your runtime:\\\\n' +\n ' - Bun: (app, port) => Bun.serve({ port, fetch: app.fetch })\\\\n' +\n ' - Node: (app, port) => serve({ fetch: app.fetch, port })'\n );\n }\n\n const port = options.port ?? Number(process.env.PORT) ?? 3000;\n let server: unknown;\n${gracefulShutdownCode}${subscriberSetup}\n logger.info({ port }, 'Starting production server');\n\n // Start HTTP server using provided serve function\n server = await options.serve(honoApp, port);\n\n logger.info({ port }, 'Production server started');\n${includeSubscribers ? '\\n await subscribeForPush(port);' : ''}\n }\n };\n}\n\n// Default export for convenience\nexport default createApp;\n`;\n\n\t\tawait writeFile(appPath, content);\n\n\t\t// Also generate the production server entry point\n\t\tawait this.generateProductionServerEntry(outputDir, context);\n\n\t\treturn appPath;\n\t}\n\n\t/**\n\t * Generate production server.ts entry point\n\t */\n\tprivate async generateProductionServerEntry(\n\t\toutputDir: string,\n\t\tcontext: BuildContext,\n\t): Promise<void> {\n\t\tconst serverPath = join(outputDir, 'server.ts');\n\n\t\tawait writeFile(\n\t\t\tjoin(outputDir, 'telemetry.ts'),\n\t\t\tgenerateTelemetryModule(context.telemetry),\n\t\t);\n\n\t\tconst content = `#!/usr/bin/env node\n/**\n * Production server entry point\n * Generated by 'gkm build --production'\n */\nimport { serve } from '@hono/node-server';\nimport { Hono } from 'hono';\nimport { startTelemetry } from './telemetry.js';\n\n// Before the app is imported, so the libraries it loads are instrumented.\nconst requestSpans = await startTelemetry({\n ignorePaths: [${JSON.stringify(context.production?.healthCheck ?? '/health')}, '/ready'],\n});\n\nconst { createApp } = await import('./app.js');\n\nconst port = Number(process.env.PORT) || 3000;\n\n// The span middleware goes on before any route, so every request runs in it.\nconst app = new Hono();\nif (requestSpans) app.use('*', requestSpans);\n\nconst { start } = await createApp(app);\n\nawait start({\n port,\n serve: (app, port) => serve({ fetch: app.fetch, port }),\n});\n`;\n\n\t\tawait writeFile(serverPath, content);\n\t}\n}\n"],"mappings":"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"}
|