@geekmidas/cli 0.4.0 → 0.5.1
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/CronGenerator-BPTqNYOR.d.cts +14 -0
- package/dist/{CronGenerator-Bh26MaNA.mjs → CronGenerator-CCRYptuT.mjs} +2 -2
- package/dist/{CronGenerator-Bh26MaNA.mjs.map → CronGenerator-CCRYptuT.mjs.map} +1 -1
- package/dist/{CronGenerator-C6MF8rlG.cjs → CronGenerator-D4TWXQbh.cjs} +2 -2
- package/dist/{CronGenerator-C6MF8rlG.cjs.map → CronGenerator-D4TWXQbh.cjs.map} +1 -1
- package/dist/CronGenerator-YAj59JUd.d.mts +14 -0
- package/dist/EndpointGenerator-ChAD1INz.d.cts +19 -0
- package/dist/EndpointGenerator-Cj3O1U8-.d.mts +19 -0
- package/dist/{EndpointGenerator-CzDhG7Or.mjs → EndpointGenerator-DGivkPLT.mjs} +22 -6
- package/dist/EndpointGenerator-DGivkPLT.mjs.map +1 -0
- package/dist/{EndpointGenerator-BxNCkus4.cjs → EndpointGenerator-npWEDoK2.cjs} +22 -6
- package/dist/EndpointGenerator-npWEDoK2.cjs.map +1 -0
- package/dist/FunctionGenerator-429-9NER.d.cts +14 -0
- package/dist/FunctionGenerator-BQ4ehoID.d.mts +14 -0
- package/dist/{FunctionGenerator-BNE_GC7N.mjs → FunctionGenerator-CVk0h8tO.mjs} +2 -2
- package/dist/{FunctionGenerator-BNE_GC7N.mjs.map → FunctionGenerator-CVk0h8tO.mjs.map} +1 -1
- package/dist/{FunctionGenerator-FgZUTd8L.cjs → FunctionGenerator-DYTnyr4c.cjs} +2 -2
- package/dist/{FunctionGenerator-FgZUTd8L.cjs.map → FunctionGenerator-DYTnyr4c.cjs.map} +1 -1
- package/dist/Generator-BjHK_qce.d.mts +27 -0
- package/dist/{Generator-UanJW0_V.mjs → Generator-CDt4pB3W.mjs} +1 -1
- package/dist/{Generator-UanJW0_V.mjs.map → Generator-CDt4pB3W.mjs.map} +1 -1
- package/dist/{Generator-CDoEXCDg.cjs → Generator-CLVplqm2.cjs} +1 -1
- package/dist/{Generator-CDoEXCDg.cjs.map → Generator-CLVplqm2.cjs.map} +1 -1
- package/dist/Generator-DxQMCQp7.d.cts +27 -0
- package/dist/{OpenApiTsGenerator-NBNEoaeO.cjs → OpenApiTsGenerator-Be-sKGTT.cjs} +1 -1
- package/dist/{OpenApiTsGenerator-NBNEoaeO.cjs.map → OpenApiTsGenerator-Be-sKGTT.cjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-q3aWNkuM.mjs → OpenApiTsGenerator-C4mHHaku.mjs} +1 -1
- package/dist/{OpenApiTsGenerator-q3aWNkuM.mjs.map → OpenApiTsGenerator-C4mHHaku.mjs.map} +1 -1
- package/dist/SubscriberGenerator-7uX42xyG.d.mts +15 -0
- package/dist/{SubscriberGenerator-Dnlj_1FK.mjs → SubscriberGenerator-DABaJXML.mjs} +2 -2
- package/dist/{SubscriberGenerator-Dnlj_1FK.mjs.map → SubscriberGenerator-DABaJXML.mjs.map} +1 -1
- package/dist/{SubscriberGenerator-Bd-a7aiw.cjs → SubscriberGenerator-D_zpNGFr.cjs} +2 -2
- package/dist/{SubscriberGenerator-Bd-a7aiw.cjs.map → SubscriberGenerator-D_zpNGFr.cjs.map} +1 -1
- package/dist/SubscriberGenerator-Dtb3HS4i.d.cts +15 -0
- package/dist/api-B3SCEHPf.cjs +190 -0
- package/dist/api-B3SCEHPf.cjs.map +1 -0
- package/dist/api-BKIN0s0S.mjs +184 -0
- package/dist/api-BKIN0s0S.mjs.map +1 -0
- package/dist/build/index.cjs +11 -10
- package/dist/build/index.d.cts +7 -0
- package/dist/build/index.d.mts +7 -0
- package/dist/build/index.mjs +11 -10
- package/dist/build/manifests.cjs +1 -1
- package/dist/build/manifests.d.cts +13 -0
- package/dist/build/manifests.d.mts +13 -0
- package/dist/build/manifests.mjs +1 -1
- package/dist/build/providerResolver.cjs +1 -1
- package/dist/build/providerResolver.d.cts +23 -0
- package/dist/build/providerResolver.d.mts +23 -0
- package/dist/build/providerResolver.mjs +1 -1
- package/dist/build/types.d.cts +3 -0
- package/dist/build/types.d.mts +3 -0
- package/dist/{build-DyDgu_D1.mjs → build-B8C_qHir.mjs} +15 -13
- package/dist/build-B8C_qHir.mjs.map +1 -0
- package/dist/{build-CWtHnJMQ.cjs → build-D0Wr49bf.cjs} +15 -13
- package/dist/build-D0Wr49bf.cjs.map +1 -0
- package/dist/config-Ba-Gbpbc.d.cts +11 -0
- package/dist/config-Bq72aj8e.mjs +75 -0
- package/dist/config-Bq72aj8e.mjs.map +1 -0
- package/dist/config-CFls09Ey.cjs +93 -0
- package/dist/config-CFls09Ey.cjs.map +1 -0
- package/dist/config-CLEDqKO3.cjs +157 -0
- package/dist/config-CLEDqKO3.cjs.map +1 -0
- package/dist/config-DBsmMDhf.d.mts +11 -0
- package/dist/config-Dp8RonV_.mjs +151 -0
- package/dist/config-Dp8RonV_.mjs.map +1 -0
- package/dist/config.cjs +4 -2
- package/dist/config.d.cts +48 -0
- package/dist/config.d.mts +48 -0
- package/dist/config.mjs +2 -2
- package/dist/dev/index.cjs +10 -9
- package/dist/dev/index.d.cts +36 -0
- package/dist/dev/index.d.mts +36 -0
- package/dist/dev/index.mjs +10 -10
- package/dist/{dev-CpA8AQPX.mjs → dev-B734w3L1.mjs} +93 -49
- package/dist/dev-B734w3L1.mjs.map +1 -0
- package/dist/{dev-CgDYC4o8.cjs → dev-DHqYn8k4.cjs} +71 -21
- package/dist/dev-DHqYn8k4.cjs.map +1 -0
- package/dist/docker-5d8Yh5_X.cjs +119 -0
- package/dist/docker-5d8Yh5_X.cjs.map +1 -0
- package/dist/docker-DlUqdFle.mjs +113 -0
- package/dist/docker-DlUqdFle.mjs.map +1 -0
- package/dist/env-B-OKjgI4.cjs +144 -0
- package/dist/env-B-OKjgI4.cjs.map +1 -0
- package/dist/env-HfuJRlg5.d.cts +11 -0
- package/dist/env-nd-iQPYM.d.mts +11 -0
- package/dist/env-tv1HlZlw.mjs +138 -0
- package/dist/env-tv1HlZlw.mjs.map +1 -0
- package/dist/generators/CronGenerator.cjs +2 -2
- package/dist/generators/CronGenerator.d.cts +5 -0
- package/dist/generators/CronGenerator.d.mts +5 -0
- package/dist/generators/CronGenerator.mjs +2 -2
- package/dist/generators/EndpointGenerator.cjs +2 -2
- package/dist/generators/EndpointGenerator.d.cts +5 -0
- package/dist/generators/EndpointGenerator.d.mts +5 -0
- package/dist/generators/EndpointGenerator.mjs +2 -2
- package/dist/generators/FunctionGenerator.cjs +2 -2
- package/dist/generators/FunctionGenerator.d.cts +5 -0
- package/dist/generators/FunctionGenerator.d.mts +5 -0
- package/dist/generators/FunctionGenerator.mjs +2 -2
- package/dist/generators/Generator.cjs +1 -1
- package/dist/generators/Generator.d.cts +4 -0
- package/dist/generators/Generator.d.mts +4 -0
- package/dist/generators/Generator.mjs +1 -1
- package/dist/generators/OpenApiTsGenerator.cjs +1 -1
- package/dist/generators/OpenApiTsGenerator.d.cts +44 -0
- package/dist/generators/OpenApiTsGenerator.d.mts +44 -0
- package/dist/generators/OpenApiTsGenerator.mjs +1 -1
- package/dist/generators/SubscriberGenerator.cjs +2 -2
- package/dist/generators/SubscriberGenerator.d.cts +5 -0
- package/dist/generators/SubscriberGenerator.d.mts +5 -0
- package/dist/generators/SubscriberGenerator.mjs +2 -2
- package/dist/generators/index.cjs +6 -6
- package/dist/generators/index.d.cts +8 -0
- package/dist/generators/index.d.mts +8 -0
- package/dist/generators/index.mjs +6 -6
- package/dist/index-C523No_B.d.mts +64 -0
- package/dist/index-DrzN4xkQ.d.cts +64 -0
- package/dist/index.cjs +48 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +48 -16
- package/dist/index.mjs.map +1 -1
- package/dist/init/generators/config.cjs +3 -0
- package/dist/init/generators/config.d.cts +3 -0
- package/dist/init/generators/config.d.mts +3 -0
- package/dist/init/generators/config.mjs +3 -0
- package/dist/init/generators/docker.cjs +3 -0
- package/dist/init/generators/docker.d.cts +11 -0
- package/dist/init/generators/docker.d.mts +11 -0
- package/dist/init/generators/docker.mjs +3 -0
- package/dist/init/generators/env.cjs +3 -0
- package/dist/init/generators/env.d.cts +3 -0
- package/dist/init/generators/env.d.mts +3 -0
- package/dist/init/generators/env.mjs +3 -0
- package/dist/init/generators/index.cjs +9 -0
- package/dist/init/generators/index.d.cts +6 -0
- package/dist/init/generators/index.d.mts +6 -0
- package/dist/init/generators/index.mjs +6 -0
- package/dist/init/generators/models.cjs +3 -0
- package/dist/init/generators/models.d.cts +11 -0
- package/dist/init/generators/models.d.mts +11 -0
- package/dist/init/generators/models.mjs +3 -0
- package/dist/init/generators/monorepo.cjs +3 -0
- package/dist/init/generators/monorepo.d.cts +11 -0
- package/dist/init/generators/monorepo.d.mts +11 -0
- package/dist/init/generators/monorepo.mjs +3 -0
- package/dist/init/generators/package.cjs +3 -0
- package/dist/init/generators/package.d.cts +3 -0
- package/dist/init/generators/package.d.mts +3 -0
- package/dist/init/generators/package.mjs +3 -0
- package/dist/init/generators/source.cjs +3 -0
- package/dist/init/generators/source.d.cts +3 -0
- package/dist/init/generators/source.d.mts +3 -0
- package/dist/init/generators/source.mjs +3 -0
- package/dist/init/index.cjs +16 -0
- package/dist/init/index.d.cts +17 -0
- package/dist/init/index.d.mts +17 -0
- package/dist/init/index.mjs +16 -0
- package/dist/init/templates/api.cjs +3 -0
- package/dist/init/templates/api.d.cts +7 -0
- package/dist/init/templates/api.d.mts +7 -0
- package/dist/init/templates/api.mjs +3 -0
- package/dist/init/templates/index.cjs +10 -0
- package/dist/init/templates/index.d.cts +2 -0
- package/dist/init/templates/index.d.mts +2 -0
- package/dist/init/templates/index.mjs +7 -0
- package/dist/init/templates/minimal.cjs +3 -0
- package/dist/init/templates/minimal.d.cts +7 -0
- package/dist/init/templates/minimal.d.mts +7 -0
- package/dist/init/templates/minimal.mjs +3 -0
- package/dist/init/templates/serverless.cjs +3 -0
- package/dist/init/templates/serverless.d.cts +7 -0
- package/dist/init/templates/serverless.d.mts +7 -0
- package/dist/init/templates/serverless.mjs +3 -0
- package/dist/init/templates/worker.cjs +3 -0
- package/dist/init/templates/worker.d.cts +7 -0
- package/dist/init/templates/worker.d.mts +7 -0
- package/dist/init/templates/worker.mjs +3 -0
- package/dist/init/utils.cjs +7 -0
- package/dist/init/utils.d.cts +25 -0
- package/dist/init/utils.d.mts +25 -0
- package/dist/init/utils.mjs +3 -0
- package/dist/init-CtOnZn3G.mjs +145 -0
- package/dist/init-CtOnZn3G.mjs.map +1 -0
- package/dist/init-qLFsWR-R.cjs +151 -0
- package/dist/init-qLFsWR-R.cjs.map +1 -0
- package/dist/{manifests-C2eMoMUm.mjs → manifests-DIA_2QYd.mjs} +1 -1
- package/dist/{manifests-C2eMoMUm.mjs.map → manifests-DIA_2QYd.mjs.map} +1 -1
- package/dist/{manifests-CK1VV_pM.cjs → manifests-VJ9-2JpW.cjs} +1 -1
- package/dist/{manifests-CK1VV_pM.cjs.map → manifests-VJ9-2JpW.cjs.map} +1 -1
- package/dist/minimal-Bdhhpp7v.cjs +93 -0
- package/dist/minimal-Bdhhpp7v.cjs.map +1 -0
- package/dist/minimal-C4GsE45s.mjs +87 -0
- package/dist/minimal-C4GsE45s.mjs.map +1 -0
- package/dist/models-DyNwdOcz.cjs +121 -0
- package/dist/models-DyNwdOcz.cjs.map +1 -0
- package/dist/models-cvNg6Oea.mjs +115 -0
- package/dist/models-cvNg6Oea.mjs.map +1 -0
- package/dist/monorepo-Cknwzj5C.mjs +184 -0
- package/dist/monorepo-Cknwzj5C.mjs.map +1 -0
- package/dist/monorepo-sEK8gW59.cjs +190 -0
- package/dist/monorepo-sEK8gW59.cjs.map +1 -0
- package/dist/{openapi-DhK4b0lB.cjs → openapi-BQWPWyNB.cjs} +4 -4
- package/dist/{openapi-DhK4b0lB.cjs.map → openapi-BQWPWyNB.cjs.map} +1 -1
- package/dist/{openapi-DRTRGhTt.mjs → openapi-DBX8cJJ8.mjs} +4 -4
- package/dist/{openapi-DRTRGhTt.mjs.map → openapi-DBX8cJJ8.mjs.map} +1 -1
- package/dist/{openapi-react-query-D9Z7lh0p.cjs → openapi-react-query-DxHjXQvg.cjs} +1 -1
- package/dist/{openapi-react-query-D9Z7lh0p.cjs.map → openapi-react-query-DxHjXQvg.cjs.map} +1 -1
- package/dist/{openapi-react-query-MEBlYIM1.mjs → openapi-react-query-o7Mp1Jd5.mjs} +1 -1
- package/dist/{openapi-react-query-MEBlYIM1.mjs.map → openapi-react-query-o7Mp1Jd5.mjs.map} +1 -1
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.d.cts +11 -0
- package/dist/openapi-react-query.d.mts +11 -0
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.cjs +5 -5
- package/dist/openapi.d.cts +11 -0
- package/dist/openapi.d.mts +11 -0
- package/dist/openapi.mjs +5 -5
- package/dist/package-C7WhWU8m.d.mts +11 -0
- package/dist/package-CIfmeuSW.mjs +51 -0
- package/dist/package-CIfmeuSW.mjs.map +1 -0
- package/dist/package-DvWEMz6z.d.cts +11 -0
- package/dist/package-PP-o1nvq.cjs +57 -0
- package/dist/package-PP-o1nvq.cjs.map +1 -0
- package/dist/{providerResolver-B_TjNF0_.mjs → providerResolver-DEVKngbC.mjs} +1 -1
- package/dist/{providerResolver-B_TjNF0_.mjs.map → providerResolver-DEVKngbC.mjs.map} +1 -1
- package/dist/{providerResolver-DgvzNfP4.cjs → providerResolver-DOTbN9jo.cjs} +1 -1
- package/dist/{providerResolver-DgvzNfP4.cjs.map → providerResolver-DOTbN9jo.cjs.map} +1 -1
- package/dist/serverless-DkHBF2vC.mjs +108 -0
- package/dist/serverless-DkHBF2vC.mjs.map +1 -0
- package/dist/serverless-Yav3GRVz.cjs +114 -0
- package/dist/serverless-Yav3GRVz.cjs.map +1 -0
- package/dist/source-D6v2BnKT.d.mts +11 -0
- package/dist/source-D8fK9qRo.d.cts +11 -0
- package/dist/source-DT5Xhiob.cjs +17 -0
- package/dist/source-DT5Xhiob.cjs.map +1 -0
- package/dist/source-DnaH_MLA.mjs +11 -0
- package/dist/source-DnaH_MLA.mjs.map +1 -0
- package/dist/templates-CBFUwpBy.mjs +64 -0
- package/dist/templates-CBFUwpBy.mjs.map +1 -0
- package/dist/templates-DM_rtYYW.cjs +87 -0
- package/dist/templates-DM_rtYYW.cjs.map +1 -0
- package/dist/types-C4KITv-y.d.mts +51 -0
- package/dist/types-Cxl8-uwV.d.mts +129 -0
- package/dist/types-DB99_qIy.d.cts +129 -0
- package/dist/types-DLFN49M3.d.cts +51 -0
- package/dist/types.d.cts +2 -0
- package/dist/types.d.mts +2 -0
- package/dist/utils-BX3F4fT8.cjs +99 -0
- package/dist/utils-BX3F4fT8.cjs.map +1 -0
- package/dist/utils-C31-SWHP.mjs +69 -0
- package/dist/utils-C31-SWHP.mjs.map +1 -0
- package/dist/worker--8O5a3Hv.cjs +150 -0
- package/dist/worker--8O5a3Hv.cjs.map +1 -0
- package/dist/worker-Jme7uOOJ.mjs +144 -0
- package/dist/worker-Jme7uOOJ.mjs.map +1 -0
- package/package.json +12 -4
- package/src/__tests__/loadEnvFiles.spec.ts +131 -0
- package/src/build/index.ts +14 -16
- package/src/build/types.ts +5 -0
- package/src/config.ts +57 -0
- package/src/dev/__tests__/index.spec.ts +114 -119
- package/src/dev/index.ts +77 -17
- package/src/generators/EndpointGenerator.ts +37 -8
- package/src/index.ts +26 -0
- package/src/init/__tests__/generators.spec.ts +366 -0
- package/src/init/__tests__/init.spec.ts +341 -0
- package/src/init/__tests__/utils.spec.ts +104 -0
- package/src/init/generators/config.ts +192 -0
- package/src/init/generators/docker.ts +134 -0
- package/src/init/generators/env.ts +182 -0
- package/src/init/generators/index.ts +4 -0
- package/src/init/generators/models.ts +129 -0
- package/src/init/generators/monorepo.ts +211 -0
- package/src/init/generators/package.ts +81 -0
- package/src/init/generators/source.ts +15 -0
- package/src/init/index.ts +206 -0
- package/src/init/templates/api.ts +218 -0
- package/src/init/templates/index.ts +108 -0
- package/src/init/templates/minimal.ts +102 -0
- package/src/init/templates/serverless.ts +129 -0
- package/src/init/templates/worker.ts +169 -0
- package/src/init/utils.ts +98 -0
- package/src/types.ts +22 -2
- package/tsdown.config.ts +1 -1
- package/dist/EndpointGenerator-BxNCkus4.cjs.map +0 -1
- package/dist/EndpointGenerator-CzDhG7Or.mjs.map +0 -1
- package/dist/build-CWtHnJMQ.cjs.map +0 -1
- package/dist/build-DyDgu_D1.mjs.map +0 -1
- package/dist/config-AFmFKmU0.mjs +0 -30
- package/dist/config-AFmFKmU0.mjs.map +0 -1
- package/dist/config-BVIJpAsa.cjs +0 -36
- package/dist/config-BVIJpAsa.cjs.map +0 -1
- package/dist/dev-CgDYC4o8.cjs.map +0 -1
- package/dist/dev-CpA8AQPX.mjs.map +0 -1
- /package/dist/{generators-CEKtVh81.cjs → generators-3IemvCLk.cjs} +0 -0
- /package/dist/{generators-CsLujGXs.mjs → generators-FNpdfN6J.mjs} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OpenApiTsGenerator-NBNEoaeO.cjs","names":["endpoints: Endpoint<any, any, any, any, any, any>[]","options: OpenApiTsOptions","endpointInfos: EndpointInfo[]","type: string","_name: string","authMap: Record<string, string | null>","interfaces: string[]","schema: StandardSchemaV1","defaultName: string","info: EndpointInfo","str: string","name: string","collectedDefs: Map<string, JsonSchema>","StandardSchemaJsonSchema","schema: JsonSchema","props: string[]","s: JsonSchema","e: string","pathEntries: string[]","methodEntries: string[]","parts: string[]","params: {\n title: string;\n version: string;\n description?: string;\n securitySchemes: SecuritySchemeInfo[];\n endpointAuth: Record<string, string | null>;\n schemaInterfaces: string;\n pathsInterface: string;\n }"],"sources":["../src/generators/OpenApiTsGenerator.ts"],"sourcesContent":["import type { Endpoint } from '@geekmidas/constructs/endpoints';\nimport {\n StandardSchemaJsonSchema,\n getSchemaMetadata,\n} from '@geekmidas/schema/conversion';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\n\ninterface OpenApiTsOptions {\n title?: string;\n version?: string;\n description?: string;\n}\n\n// JSON Schema type definition\ninterface JsonSchema {\n type?: string;\n properties?: Record<string, JsonSchema>;\n items?: JsonSchema;\n required?: string[];\n enum?: string[];\n $ref?: string;\n anyOf?: JsonSchema[];\n oneOf?: JsonSchema[];\n allOf?: JsonSchema[];\n additionalProperties?: boolean | JsonSchema;\n [key: string]: unknown;\n}\n\n// Security scheme type (OpenAPI 3.1)\ninterface SecuritySchemeObject {\n type: 'apiKey' | 'http' | 'mutualTLS' | 'oauth2' | 'openIdConnect';\n description?: string;\n name?: string;\n in?: 'query' | 'header' | 'cookie';\n scheme?: string;\n bearerFormat?: string;\n flows?: Record<string, unknown>;\n openIdConnectUrl?: string;\n [key: string]: unknown;\n}\n\ninterface EndpointInfo {\n endpoint: string;\n route: string;\n method: string;\n authorizerName: string | null;\n /** @deprecated Use securityScheme instead */\n authorizerType: string | null;\n /** The OpenAPI security scheme definition for this endpoint's authorizer */\n securityScheme: SecuritySchemeObject | null;\n input?: {\n body?: StandardSchemaV1;\n query?: StandardSchemaV1;\n params?: StandardSchemaV1;\n };\n output?: StandardSchemaV1;\n description?: string;\n tags?: string[];\n operationId?: string;\n}\n\ninterface SecuritySchemeInfo {\n name: string;\n type: string;\n scheme: SecuritySchemeObject;\n}\n\n/**\n * Generates TypeScript OpenAPI module from endpoints.\n * Outputs:\n * - securitySchemes: typed security scheme definitions\n * - endpointAuth: runtime map of endpoints to auth requirements\n * - paths: TypeScript interface for type-safe fetcher\n * - schema interfaces: reusable TypeScript types from Zod/Valibot schemas\n */\nexport class OpenApiTsGenerator {\n async generate(\n endpoints: Endpoint<any, any, any, any, any, any>[],\n options: OpenApiTsOptions = {},\n ): Promise<string> {\n const { title = 'API', version = '1.0.0', description } = options;\n\n // Extract endpoint info\n const endpointInfos = await this.extractEndpointInfos(endpoints);\n\n // Collect unique security schemes\n const securitySchemes = this.collectSecuritySchemes(endpointInfos);\n\n // Build endpoint auth map\n const endpointAuth = this.buildEndpointAuthMap(endpointInfos);\n\n // Generate schema interfaces\n const schemaInterfaces = await this.generateSchemaInterfaces(endpointInfos);\n\n // Generate paths interface\n const pathsInterface = await this.generatePathsInterface(endpointInfos);\n\n // Build the final TypeScript module\n return this.buildModule({\n title,\n version,\n description,\n securitySchemes,\n endpointAuth,\n schemaInterfaces,\n pathsInterface,\n });\n }\n\n private async extractEndpointInfos(\n endpoints: Endpoint<any, any, any, any, any, any>[],\n ): Promise<EndpointInfo[]> {\n return endpoints.map((ep) => {\n const route = ep.route.replace(/:(\\w+)/g, '{$1}');\n const method = ep.method.toUpperCase();\n\n // Get security scheme from authorizer (if available)\n // This is the preferred way - the scheme is stored directly on the authorizer\n const securityScheme = ep.authorizer?.securityScheme as\n | SecuritySchemeObject\n | undefined;\n\n return {\n endpoint: `${method} ${route}`,\n route,\n method,\n authorizerName: ep.authorizer?.name ?? null,\n authorizerType: ep.authorizer?.type ?? null,\n securityScheme: securityScheme ?? null,\n input: ep.input,\n output: ep.outputSchema,\n description: ep.description,\n tags: ep.tags,\n operationId: ep.operationId,\n };\n });\n }\n\n private collectSecuritySchemes(\n endpointInfos: EndpointInfo[],\n ): SecuritySchemeInfo[] {\n const schemes = new Map<string, SecuritySchemeInfo>();\n\n for (const info of endpointInfos) {\n if (info.authorizerName && !schemes.has(info.authorizerName)) {\n // Prefer the stored security scheme (from .securitySchemes() or built-ins)\n // Fall back to inference from authorizerType for backward compatibility\n const scheme =\n info.securityScheme ??\n (info.authorizerType\n ? this.mapAuthorizerToSecurityScheme(\n info.authorizerType,\n info.authorizerName,\n )\n : null);\n\n if (scheme) {\n schemes.set(info.authorizerName, {\n name: info.authorizerName,\n type: scheme.type,\n scheme,\n });\n }\n }\n }\n\n return Array.from(schemes.values());\n }\n\n private mapAuthorizerToSecurityScheme(\n type: string,\n _name: string,\n ): SecuritySchemeObject {\n switch (type.toLowerCase()) {\n case 'jwt':\n case 'bearer':\n return {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n };\n case 'iam':\n case 'aws-sigv4':\n case 'sigv4':\n return {\n type: 'apiKey',\n in: 'header',\n name: 'Authorization',\n 'x-amazon-apigateway-authtype': 'awsSigv4',\n };\n case 'apikey':\n case 'api-key':\n return {\n type: 'apiKey',\n in: 'header',\n name: 'X-API-Key',\n };\n case 'oauth2':\n return {\n type: 'oauth2',\n flows: {},\n };\n case 'oidc':\n case 'openidconnect':\n return {\n type: 'openIdConnect',\n openIdConnectUrl: '',\n };\n default:\n return {\n type: 'http',\n scheme: 'bearer',\n };\n }\n }\n\n private buildEndpointAuthMap(\n endpointInfos: EndpointInfo[],\n ): Record<string, string | null> {\n const authMap: Record<string, string | null> = {};\n\n for (const info of endpointInfos) {\n authMap[info.endpoint] = info.authorizerName;\n }\n\n return authMap;\n }\n\n private async generateSchemaInterfaces(\n endpointInfos: EndpointInfo[],\n ): Promise<string> {\n const interfaces: string[] = [];\n const generatedNames = new Set<string>();\n // Collect nested schemas with $defs (from .meta({ id: 'X' }))\n const collectedDefs = new Map<string, JsonSchema>();\n\n for (const info of endpointInfos) {\n const baseName = this.getSchemaBaseName(info);\n\n // Input body schema\n if (info.input?.body) {\n const name = await this.getSchemaName(\n info.input.body,\n `${baseName}Input`,\n );\n if (!generatedNames.has(name)) {\n const schema = await this.schemaToInterfaceWithDefs(\n info.input.body,\n name,\n collectedDefs,\n );\n if (schema) {\n interfaces.push(schema);\n generatedNames.add(name);\n }\n }\n }\n\n // Input params schema\n if (info.input?.params) {\n const name = await this.getSchemaName(\n info.input.params,\n `${baseName}Params`,\n );\n if (!generatedNames.has(name)) {\n const schema = await this.schemaToInterfaceWithDefs(\n info.input.params,\n name,\n collectedDefs,\n );\n if (schema) {\n interfaces.push(schema);\n generatedNames.add(name);\n }\n }\n }\n\n // Input query schema\n if (info.input?.query) {\n const name = await this.getSchemaName(\n info.input.query,\n `${baseName}Query`,\n );\n if (!generatedNames.has(name)) {\n const schema = await this.schemaToInterfaceWithDefs(\n info.input.query,\n name,\n collectedDefs,\n );\n if (schema) {\n interfaces.push(schema);\n generatedNames.add(name);\n }\n }\n }\n\n // Output schema\n if (info.output) {\n const name = await this.getSchemaName(info.output, `${baseName}Output`);\n if (!generatedNames.has(name)) {\n const schema = await this.schemaToInterfaceWithDefs(\n info.output,\n name,\n collectedDefs,\n );\n if (schema) {\n interfaces.push(schema);\n generatedNames.add(name);\n }\n }\n }\n }\n\n // Generate interfaces for collected $defs (nested schemas with .meta({ id: 'X' }))\n for (const [defName, defSchema] of collectedDefs) {\n if (!generatedNames.has(defName)) {\n const interfaceStr = this.jsonSchemaToInterface(defSchema, defName);\n interfaces.push(interfaceStr);\n generatedNames.add(defName);\n }\n }\n\n return interfaces.join('\\n\\n');\n }\n\n /**\n * Get the name for a schema, using metadata `id` if available,\n * otherwise falling back to the provided default name.\n */\n private async getSchemaName(\n schema: StandardSchemaV1,\n defaultName: string,\n ): Promise<string> {\n try {\n const metadata = await getSchemaMetadata(schema);\n if (metadata?.id) {\n return this.pascalCase(metadata.id);\n }\n } catch {\n // Ignore metadata extraction errors\n }\n return defaultName;\n }\n\n private getSchemaBaseName(info: EndpointInfo): string {\n if (info.operationId) {\n return this.pascalCase(info.operationId);\n }\n\n // Generate name from method + route\n const routeParts = info.route\n .replace(/[{}]/g, '')\n .split('/')\n .filter(Boolean)\n .map((part) => this.pascalCase(part));\n\n return `${this.pascalCase(info.method.toLowerCase())}${routeParts.join('')}`;\n }\n\n private pascalCase(str: string): string {\n return str\n .replace(/[-_](.)/g, (_, c) => c.toUpperCase())\n .replace(/^./, (c) => c.toUpperCase());\n }\n\n /**\n * Convert schema to interface while collecting $defs for nested schemas\n * with .meta({ id: 'X' }).\n */\n private async schemaToInterfaceWithDefs(\n schema: StandardSchemaV1,\n name: string,\n collectedDefs: Map<string, JsonSchema>,\n ): Promise<string | null> {\n try {\n // Get raw JSON schema with $defs intact (don't use convertStandardSchemaToJsonSchema\n // which strips $defs)\n const vendor = schema['~standard']?.vendor;\n if (!vendor || !(vendor in StandardSchemaJsonSchema)) {\n return null;\n }\n\n const toJsonSchema =\n StandardSchemaJsonSchema[\n vendor as keyof typeof StandardSchemaJsonSchema\n ];\n const jsonSchema = await toJsonSchema(schema);\n if (!jsonSchema) return null;\n\n // Extract $defs from the JSON schema (these come from .meta({ id: 'X' }))\n if (jsonSchema.$defs && typeof jsonSchema.$defs === 'object') {\n for (const [defName, defSchema] of Object.entries(jsonSchema.$defs)) {\n if (!collectedDefs.has(defName)) {\n // Remove the 'id' field from the schema as it's just metadata\n const { id, ...schemaWithoutId } = defSchema as JsonSchema & {\n id?: string;\n };\n collectedDefs.set(defName, schemaWithoutId as JsonSchema);\n }\n }\n }\n\n // Remove $defs from the schema before converting to interface\n const { $defs, ...schemaWithoutDefs } = jsonSchema;\n return this.jsonSchemaToInterface(schemaWithoutDefs, name);\n } catch {\n return null;\n }\n }\n\n private jsonSchemaToInterface(schema: JsonSchema, name: string): string {\n if (schema.type !== 'object' || !schema.properties) {\n // For non-object types, create a type alias\n const typeStr = this.jsonSchemaTypeToTs(schema);\n return `export type ${name} = ${typeStr};`;\n }\n\n const props: string[] = [];\n const required = new Set(schema.required || []);\n\n for (const [propName, propSchema] of Object.entries(schema.properties)) {\n const isRequired = required.has(propName);\n const typeStr = this.jsonSchemaTypeToTs(propSchema as JsonSchema);\n const optionalMark = isRequired ? '' : '?';\n props.push(` ${propName}${optionalMark}: ${typeStr};`);\n }\n\n return `export interface ${name} {\\n${props.join('\\n')}\\n}`;\n }\n\n private jsonSchemaTypeToTs(schema: JsonSchema): string {\n if (!schema) return 'unknown';\n\n if (schema.$ref) {\n // Extract name from $ref\n const refName = schema.$ref.split('/').pop() || 'unknown';\n return refName;\n }\n\n if (schema.anyOf) {\n return schema.anyOf\n .map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n .join(' | ');\n }\n\n if (schema.oneOf) {\n return schema.oneOf\n .map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n .join(' | ');\n }\n\n if (schema.allOf) {\n return schema.allOf\n .map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n .join(' & ');\n }\n\n switch (schema.type) {\n case 'string':\n if (schema.enum) {\n return schema.enum.map((e: string) => `'${e}'`).join(' | ');\n }\n return 'string';\n case 'number':\n case 'integer':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'null':\n return 'null';\n case 'array':\n if (schema.items) {\n return `Array<${this.jsonSchemaTypeToTs(schema.items as JsonSchema)}>`;\n }\n return 'Array<unknown>';\n case 'object':\n if (schema.properties) {\n const props: string[] = [];\n const required = new Set(schema.required || []);\n for (const [propName, propSchema] of Object.entries(\n schema.properties,\n )) {\n const isRequired = required.has(propName);\n const typeStr = this.jsonSchemaTypeToTs(propSchema as JsonSchema);\n const optionalMark = isRequired ? '' : '?';\n props.push(`${propName}${optionalMark}: ${typeStr}`);\n }\n return `{ ${props.join('; ')} }`;\n }\n if (schema.additionalProperties) {\n const valueType = this.jsonSchemaTypeToTs(\n schema.additionalProperties as JsonSchema,\n );\n return `Record<string, ${valueType}>`;\n }\n return 'Record<string, unknown>';\n default:\n return 'unknown';\n }\n }\n\n private async generatePathsInterface(\n endpointInfos: EndpointInfo[],\n ): Promise<string> {\n const pathGroups = new Map<string, EndpointInfo[]>();\n\n // Group endpoints by route\n for (const info of endpointInfos) {\n const existing = pathGroups.get(info.route) || [];\n existing.push(info);\n pathGroups.set(info.route, existing);\n }\n\n const pathEntries: string[] = [];\n\n for (const [route, infos] of pathGroups) {\n const methodEntries: string[] = [];\n\n for (const info of infos) {\n const methodDef = await this.generateMethodDefinition(info);\n methodEntries.push(` ${info.method.toLowerCase()}: ${methodDef};`);\n }\n\n // Add path parameters if present\n const firstWithParams = infos.find((i) => i.input?.params);\n let paramsEntry = '';\n if (firstWithParams?.input?.params) {\n const paramsName = await this.getSchemaName(\n firstWithParams.input.params,\n `${this.getSchemaBaseName(firstWithParams)}Params`,\n );\n paramsEntry = `\\n parameters: {\\n path: ${paramsName};\\n };`;\n }\n\n pathEntries.push(\n ` '${route}': {${paramsEntry}\\n${methodEntries.join('\\n')}\\n };`,\n );\n }\n\n return `export interface paths {\\n${pathEntries.join('\\n')}\\n}`;\n }\n\n private async generateMethodDefinition(info: EndpointInfo): Promise<string> {\n const parts: string[] = [];\n const baseName = this.getSchemaBaseName(info);\n\n // Request body\n if (info.input?.body) {\n const bodyName = await this.getSchemaName(\n info.input.body,\n `${baseName}Input`,\n );\n parts.push(`requestBody: {\n content: {\n 'application/json': ${bodyName};\n };\n }`);\n }\n\n // Query parameters\n if (info.input?.query) {\n const queryName = await this.getSchemaName(\n info.input.query,\n `${baseName}Query`,\n );\n parts.push(`parameters: {\n query: ${queryName};\n }`);\n }\n\n // Responses\n const outputName = info.output\n ? await this.getSchemaName(info.output, `${baseName}Output`)\n : 'unknown';\n parts.push(`responses: {\n 200: {\n content: {\n 'application/json': ${outputName};\n };\n };\n }`);\n\n return `{\\n ${parts.join(';\\n ')};\\n }`;\n }\n\n private buildModule(params: {\n title: string;\n version: string;\n description?: string;\n securitySchemes: SecuritySchemeInfo[];\n endpointAuth: Record<string, string | null>;\n schemaInterfaces: string;\n pathsInterface: string;\n }): string {\n const {\n title,\n version,\n description,\n securitySchemes,\n endpointAuth,\n schemaInterfaces,\n pathsInterface,\n } = params;\n\n const securitySchemesObj = securitySchemes.reduce(\n (acc, s) => {\n acc[s.name] = s.scheme;\n return acc;\n },\n {} as Record<string, SecuritySchemeObject>,\n );\n\n const schemeNames = securitySchemes.map((s) => `'${s.name}'`).join(' | ');\n\n // Generate createApi only if there are security schemes\n const hasSecuritySchemes = schemeNames.length > 0;\n\n const createApiSection = hasSecuritySchemes\n ? `\n// ============================================================\n// API Client Factory\n// ============================================================\n\nimport {\n createAuthAwareFetcher,\n type AuthStrategy,\n} from '@geekmidas/client/auth-fetcher';\nimport { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';\nimport type { QueryClient } from '@tanstack/react-query';\n\n/**\n * Options for creating the API client.\n */\nexport interface CreateApiOptions {\n /** Base URL for all API requests (required) */\n baseURL: string;\n /** Auth strategies for each security scheme used in this API */\n authStrategies: Record<SecuritySchemeId, AuthStrategy>;\n /** Optional React Query client instance */\n queryClient?: QueryClient;\n /** Optional request interceptor */\n onRequest?: (config: RequestInit) => RequestInit | Promise<RequestInit>;\n}\n\n/**\n * Create a type-safe API client with authentication and React Query hooks.\n *\n * @example\n * \\`\\`\\`typescript\n * const api = createApi({\n * baseURL: 'https://api.example.com',\n * authStrategies: {\n * jwt: { type: 'bearer', tokenProvider },\n * },\n * });\n *\n * // Imperative fetch\n * const user = await api('GET /users/{id}', { params: { id: '123' } });\n *\n * // React Query hooks\n * const { data } = api.useQuery('GET /users/{id}', { params: { id: '123' } });\n * const mutation = api.useMutation('POST /users');\n * \\`\\`\\`\n */\nexport function createApi(options: CreateApiOptions) {\n const fetcher = createAuthAwareFetcher<paths, typeof endpointAuth, typeof securitySchemes>({\n baseURL: options.baseURL,\n endpointAuth,\n securitySchemes,\n authStrategies: options.authStrategies,\n onRequest: options.onRequest,\n });\n\n const hooks = createEndpointHooks<paths>(fetcher, { queryClient: options.queryClient });\n\n return Object.assign(fetcher, hooks);\n}\n`\n : `\n// ============================================================\n// API Client Factory\n// ============================================================\n\nimport { TypedFetcher, type FetcherOptions } from '@geekmidas/client/fetcher';\nimport { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';\nimport type { QueryClient } from '@tanstack/react-query';\n\n/**\n * Options for creating the API client.\n */\nexport interface CreateApiOptions extends Omit<FetcherOptions, 'baseURL'> {\n /** Base URL for all API requests (required) */\n baseURL: string;\n /** Optional React Query client instance */\n queryClient?: QueryClient;\n}\n\n/**\n * Create a type-safe API client with React Query hooks.\n *\n * @example\n * \\`\\`\\`typescript\n * const api = createApi({\n * baseURL: 'https://api.example.com',\n * });\n *\n * // Imperative fetch\n * const data = await api('GET /health');\n *\n * // React Query hooks\n * const { data } = api.useQuery('GET /health');\n * \\`\\`\\`\n */\nexport function createApi(options: CreateApiOptions) {\n const { queryClient, ...fetcherOptions } = options;\n const fetcher = new TypedFetcher<paths>(fetcherOptions);\n\n const hooks = createEndpointHooks<paths>(fetcher.request.bind(fetcher), { queryClient });\n\n return Object.assign(fetcher.request.bind(fetcher), hooks);\n}\n`;\n\n return `// Auto-generated by @geekmidas/cli - DO NOT EDIT\n// Generated: ${new Date().toISOString()}\n\n// ============================================================\n// Security Scheme Type\n// ============================================================\n\ninterface SecuritySchemeObject {\n type: 'apiKey' | 'http' | 'mutualTLS' | 'oauth2' | 'openIdConnect';\n description?: string;\n name?: string;\n in?: 'query' | 'header' | 'cookie';\n scheme?: string;\n bearerFormat?: string;\n flows?: Record<string, unknown>;\n openIdConnectUrl?: string;\n [key: string]: unknown;\n}\n\n// ============================================================\n// API Info\n// ============================================================\n\nexport const apiInfo = {\n title: '${title}',\n version: '${version}',${description ? `\\n description: '${description.replace(/'/g, \"\\\\'\")}',` : ''}\n} as const;\n\n// ============================================================\n// Security Schemes\n// ============================================================\n\n/**\n * Available security schemes for this API.\n * Maps authorizer names to OpenAPI security scheme definitions.\n */\nexport const securitySchemes = ${JSON.stringify(securitySchemesObj, null, 2).replace(/\"([^\"]+)\":/g, '$1:')} as const satisfies Record<string, SecuritySchemeObject>;\n\nexport type SecuritySchemeId = ${schemeNames || 'never'};\n\n// ============================================================\n// Endpoint Authentication Map\n// ============================================================\n\n/**\n * Runtime map of endpoints to their required authentication scheme.\n * \\`null\\` indicates a public endpoint (no auth required).\n */\nexport const endpointAuth = ${JSON.stringify(endpointAuth, null, 2).replace(/\"([^\"]+)\":/g, \"'$1':\")} as const satisfies Record<string, SecuritySchemeId | null>;\n\nexport type EndpointString = keyof typeof endpointAuth;\n\nexport type AuthenticatedEndpoint = {\n [K in EndpointString]: typeof endpointAuth[K] extends null ? never : K;\n}[EndpointString];\n\nexport type PublicEndpoint = {\n [K in EndpointString]: typeof endpointAuth[K] extends null ? K : never;\n}[EndpointString];\n\n// ============================================================\n// Schema Definitions\n// ============================================================\n\n${schemaInterfaces}\n\n// ============================================================\n// OpenAPI Paths\n// ============================================================\n\n${pathsInterface}\n${createApiSection}\n`;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OpenApiTsGenerator-Be-sKGTT.cjs","names":["endpoints: Endpoint<any, any, any, any, any, any>[]","options: OpenApiTsOptions","endpointInfos: EndpointInfo[]","type: string","_name: string","authMap: Record<string, string | null>","interfaces: string[]","schema: StandardSchemaV1","defaultName: string","info: EndpointInfo","str: string","name: string","collectedDefs: Map<string, JsonSchema>","StandardSchemaJsonSchema","schema: JsonSchema","props: string[]","s: JsonSchema","e: string","pathEntries: string[]","methodEntries: string[]","parts: string[]","params: {\n title: string;\n version: string;\n description?: string;\n securitySchemes: SecuritySchemeInfo[];\n endpointAuth: Record<string, string | null>;\n schemaInterfaces: string;\n pathsInterface: string;\n }"],"sources":["../src/generators/OpenApiTsGenerator.ts"],"sourcesContent":["import type { Endpoint } from '@geekmidas/constructs/endpoints';\nimport {\n StandardSchemaJsonSchema,\n getSchemaMetadata,\n} from '@geekmidas/schema/conversion';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\n\ninterface OpenApiTsOptions {\n title?: string;\n version?: string;\n description?: string;\n}\n\n// JSON Schema type definition\ninterface JsonSchema {\n type?: string;\n properties?: Record<string, JsonSchema>;\n items?: JsonSchema;\n required?: string[];\n enum?: string[];\n $ref?: string;\n anyOf?: JsonSchema[];\n oneOf?: JsonSchema[];\n allOf?: JsonSchema[];\n additionalProperties?: boolean | JsonSchema;\n [key: string]: unknown;\n}\n\n// Security scheme type (OpenAPI 3.1)\ninterface SecuritySchemeObject {\n type: 'apiKey' | 'http' | 'mutualTLS' | 'oauth2' | 'openIdConnect';\n description?: string;\n name?: string;\n in?: 'query' | 'header' | 'cookie';\n scheme?: string;\n bearerFormat?: string;\n flows?: Record<string, unknown>;\n openIdConnectUrl?: string;\n [key: string]: unknown;\n}\n\ninterface EndpointInfo {\n endpoint: string;\n route: string;\n method: string;\n authorizerName: string | null;\n /** @deprecated Use securityScheme instead */\n authorizerType: string | null;\n /** The OpenAPI security scheme definition for this endpoint's authorizer */\n securityScheme: SecuritySchemeObject | null;\n input?: {\n body?: StandardSchemaV1;\n query?: StandardSchemaV1;\n params?: StandardSchemaV1;\n };\n output?: StandardSchemaV1;\n description?: string;\n tags?: string[];\n operationId?: string;\n}\n\ninterface SecuritySchemeInfo {\n name: string;\n type: string;\n scheme: SecuritySchemeObject;\n}\n\n/**\n * Generates TypeScript OpenAPI module from endpoints.\n * Outputs:\n * - securitySchemes: typed security scheme definitions\n * - endpointAuth: runtime map of endpoints to auth requirements\n * - paths: TypeScript interface for type-safe fetcher\n * - schema interfaces: reusable TypeScript types from Zod/Valibot schemas\n */\nexport class OpenApiTsGenerator {\n async generate(\n endpoints: Endpoint<any, any, any, any, any, any>[],\n options: OpenApiTsOptions = {},\n ): Promise<string> {\n const { title = 'API', version = '1.0.0', description } = options;\n\n // Extract endpoint info\n const endpointInfos = await this.extractEndpointInfos(endpoints);\n\n // Collect unique security schemes\n const securitySchemes = this.collectSecuritySchemes(endpointInfos);\n\n // Build endpoint auth map\n const endpointAuth = this.buildEndpointAuthMap(endpointInfos);\n\n // Generate schema interfaces\n const schemaInterfaces = await this.generateSchemaInterfaces(endpointInfos);\n\n // Generate paths interface\n const pathsInterface = await this.generatePathsInterface(endpointInfos);\n\n // Build the final TypeScript module\n return this.buildModule({\n title,\n version,\n description,\n securitySchemes,\n endpointAuth,\n schemaInterfaces,\n pathsInterface,\n });\n }\n\n private async extractEndpointInfos(\n endpoints: Endpoint<any, any, any, any, any, any>[],\n ): Promise<EndpointInfo[]> {\n return endpoints.map((ep) => {\n const route = ep.route.replace(/:(\\w+)/g, '{$1}');\n const method = ep.method.toUpperCase();\n\n // Get security scheme from authorizer (if available)\n // This is the preferred way - the scheme is stored directly on the authorizer\n const securityScheme = ep.authorizer?.securityScheme as\n | SecuritySchemeObject\n | undefined;\n\n return {\n endpoint: `${method} ${route}`,\n route,\n method,\n authorizerName: ep.authorizer?.name ?? null,\n authorizerType: ep.authorizer?.type ?? null,\n securityScheme: securityScheme ?? null,\n input: ep.input,\n output: ep.outputSchema,\n description: ep.description,\n tags: ep.tags,\n operationId: ep.operationId,\n };\n });\n }\n\n private collectSecuritySchemes(\n endpointInfos: EndpointInfo[],\n ): SecuritySchemeInfo[] {\n const schemes = new Map<string, SecuritySchemeInfo>();\n\n for (const info of endpointInfos) {\n if (info.authorizerName && !schemes.has(info.authorizerName)) {\n // Prefer the stored security scheme (from .securitySchemes() or built-ins)\n // Fall back to inference from authorizerType for backward compatibility\n const scheme =\n info.securityScheme ??\n (info.authorizerType\n ? this.mapAuthorizerToSecurityScheme(\n info.authorizerType,\n info.authorizerName,\n )\n : null);\n\n if (scheme) {\n schemes.set(info.authorizerName, {\n name: info.authorizerName,\n type: scheme.type,\n scheme,\n });\n }\n }\n }\n\n return Array.from(schemes.values());\n }\n\n private mapAuthorizerToSecurityScheme(\n type: string,\n _name: string,\n ): SecuritySchemeObject {\n switch (type.toLowerCase()) {\n case 'jwt':\n case 'bearer':\n return {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n };\n case 'iam':\n case 'aws-sigv4':\n case 'sigv4':\n return {\n type: 'apiKey',\n in: 'header',\n name: 'Authorization',\n 'x-amazon-apigateway-authtype': 'awsSigv4',\n };\n case 'apikey':\n case 'api-key':\n return {\n type: 'apiKey',\n in: 'header',\n name: 'X-API-Key',\n };\n case 'oauth2':\n return {\n type: 'oauth2',\n flows: {},\n };\n case 'oidc':\n case 'openidconnect':\n return {\n type: 'openIdConnect',\n openIdConnectUrl: '',\n };\n default:\n return {\n type: 'http',\n scheme: 'bearer',\n };\n }\n }\n\n private buildEndpointAuthMap(\n endpointInfos: EndpointInfo[],\n ): Record<string, string | null> {\n const authMap: Record<string, string | null> = {};\n\n for (const info of endpointInfos) {\n authMap[info.endpoint] = info.authorizerName;\n }\n\n return authMap;\n }\n\n private async generateSchemaInterfaces(\n endpointInfos: EndpointInfo[],\n ): Promise<string> {\n const interfaces: string[] = [];\n const generatedNames = new Set<string>();\n // Collect nested schemas with $defs (from .meta({ id: 'X' }))\n const collectedDefs = new Map<string, JsonSchema>();\n\n for (const info of endpointInfos) {\n const baseName = this.getSchemaBaseName(info);\n\n // Input body schema\n if (info.input?.body) {\n const name = await this.getSchemaName(\n info.input.body,\n `${baseName}Input`,\n );\n if (!generatedNames.has(name)) {\n const schema = await this.schemaToInterfaceWithDefs(\n info.input.body,\n name,\n collectedDefs,\n );\n if (schema) {\n interfaces.push(schema);\n generatedNames.add(name);\n }\n }\n }\n\n // Input params schema\n if (info.input?.params) {\n const name = await this.getSchemaName(\n info.input.params,\n `${baseName}Params`,\n );\n if (!generatedNames.has(name)) {\n const schema = await this.schemaToInterfaceWithDefs(\n info.input.params,\n name,\n collectedDefs,\n );\n if (schema) {\n interfaces.push(schema);\n generatedNames.add(name);\n }\n }\n }\n\n // Input query schema\n if (info.input?.query) {\n const name = await this.getSchemaName(\n info.input.query,\n `${baseName}Query`,\n );\n if (!generatedNames.has(name)) {\n const schema = await this.schemaToInterfaceWithDefs(\n info.input.query,\n name,\n collectedDefs,\n );\n if (schema) {\n interfaces.push(schema);\n generatedNames.add(name);\n }\n }\n }\n\n // Output schema\n if (info.output) {\n const name = await this.getSchemaName(info.output, `${baseName}Output`);\n if (!generatedNames.has(name)) {\n const schema = await this.schemaToInterfaceWithDefs(\n info.output,\n name,\n collectedDefs,\n );\n if (schema) {\n interfaces.push(schema);\n generatedNames.add(name);\n }\n }\n }\n }\n\n // Generate interfaces for collected $defs (nested schemas with .meta({ id: 'X' }))\n for (const [defName, defSchema] of collectedDefs) {\n if (!generatedNames.has(defName)) {\n const interfaceStr = this.jsonSchemaToInterface(defSchema, defName);\n interfaces.push(interfaceStr);\n generatedNames.add(defName);\n }\n }\n\n return interfaces.join('\\n\\n');\n }\n\n /**\n * Get the name for a schema, using metadata `id` if available,\n * otherwise falling back to the provided default name.\n */\n private async getSchemaName(\n schema: StandardSchemaV1,\n defaultName: string,\n ): Promise<string> {\n try {\n const metadata = await getSchemaMetadata(schema);\n if (metadata?.id) {\n return this.pascalCase(metadata.id);\n }\n } catch {\n // Ignore metadata extraction errors\n }\n return defaultName;\n }\n\n private getSchemaBaseName(info: EndpointInfo): string {\n if (info.operationId) {\n return this.pascalCase(info.operationId);\n }\n\n // Generate name from method + route\n const routeParts = info.route\n .replace(/[{}]/g, '')\n .split('/')\n .filter(Boolean)\n .map((part) => this.pascalCase(part));\n\n return `${this.pascalCase(info.method.toLowerCase())}${routeParts.join('')}`;\n }\n\n private pascalCase(str: string): string {\n return str\n .replace(/[-_](.)/g, (_, c) => c.toUpperCase())\n .replace(/^./, (c) => c.toUpperCase());\n }\n\n /**\n * Convert schema to interface while collecting $defs for nested schemas\n * with .meta({ id: 'X' }).\n */\n private async schemaToInterfaceWithDefs(\n schema: StandardSchemaV1,\n name: string,\n collectedDefs: Map<string, JsonSchema>,\n ): Promise<string | null> {\n try {\n // Get raw JSON schema with $defs intact (don't use convertStandardSchemaToJsonSchema\n // which strips $defs)\n const vendor = schema['~standard']?.vendor;\n if (!vendor || !(vendor in StandardSchemaJsonSchema)) {\n return null;\n }\n\n const toJsonSchema =\n StandardSchemaJsonSchema[\n vendor as keyof typeof StandardSchemaJsonSchema\n ];\n const jsonSchema = await toJsonSchema(schema);\n if (!jsonSchema) return null;\n\n // Extract $defs from the JSON schema (these come from .meta({ id: 'X' }))\n if (jsonSchema.$defs && typeof jsonSchema.$defs === 'object') {\n for (const [defName, defSchema] of Object.entries(jsonSchema.$defs)) {\n if (!collectedDefs.has(defName)) {\n // Remove the 'id' field from the schema as it's just metadata\n const { id, ...schemaWithoutId } = defSchema as JsonSchema & {\n id?: string;\n };\n collectedDefs.set(defName, schemaWithoutId as JsonSchema);\n }\n }\n }\n\n // Remove $defs from the schema before converting to interface\n const { $defs, ...schemaWithoutDefs } = jsonSchema;\n return this.jsonSchemaToInterface(schemaWithoutDefs, name);\n } catch {\n return null;\n }\n }\n\n private jsonSchemaToInterface(schema: JsonSchema, name: string): string {\n if (schema.type !== 'object' || !schema.properties) {\n // For non-object types, create a type alias\n const typeStr = this.jsonSchemaTypeToTs(schema);\n return `export type ${name} = ${typeStr};`;\n }\n\n const props: string[] = [];\n const required = new Set(schema.required || []);\n\n for (const [propName, propSchema] of Object.entries(schema.properties)) {\n const isRequired = required.has(propName);\n const typeStr = this.jsonSchemaTypeToTs(propSchema as JsonSchema);\n const optionalMark = isRequired ? '' : '?';\n props.push(` ${propName}${optionalMark}: ${typeStr};`);\n }\n\n return `export interface ${name} {\\n${props.join('\\n')}\\n}`;\n }\n\n private jsonSchemaTypeToTs(schema: JsonSchema): string {\n if (!schema) return 'unknown';\n\n if (schema.$ref) {\n // Extract name from $ref\n const refName = schema.$ref.split('/').pop() || 'unknown';\n return refName;\n }\n\n if (schema.anyOf) {\n return schema.anyOf\n .map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n .join(' | ');\n }\n\n if (schema.oneOf) {\n return schema.oneOf\n .map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n .join(' | ');\n }\n\n if (schema.allOf) {\n return schema.allOf\n .map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n .join(' & ');\n }\n\n switch (schema.type) {\n case 'string':\n if (schema.enum) {\n return schema.enum.map((e: string) => `'${e}'`).join(' | ');\n }\n return 'string';\n case 'number':\n case 'integer':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'null':\n return 'null';\n case 'array':\n if (schema.items) {\n return `Array<${this.jsonSchemaTypeToTs(schema.items as JsonSchema)}>`;\n }\n return 'Array<unknown>';\n case 'object':\n if (schema.properties) {\n const props: string[] = [];\n const required = new Set(schema.required || []);\n for (const [propName, propSchema] of Object.entries(\n schema.properties,\n )) {\n const isRequired = required.has(propName);\n const typeStr = this.jsonSchemaTypeToTs(propSchema as JsonSchema);\n const optionalMark = isRequired ? '' : '?';\n props.push(`${propName}${optionalMark}: ${typeStr}`);\n }\n return `{ ${props.join('; ')} }`;\n }\n if (schema.additionalProperties) {\n const valueType = this.jsonSchemaTypeToTs(\n schema.additionalProperties as JsonSchema,\n );\n return `Record<string, ${valueType}>`;\n }\n return 'Record<string, unknown>';\n default:\n return 'unknown';\n }\n }\n\n private async generatePathsInterface(\n endpointInfos: EndpointInfo[],\n ): Promise<string> {\n const pathGroups = new Map<string, EndpointInfo[]>();\n\n // Group endpoints by route\n for (const info of endpointInfos) {\n const existing = pathGroups.get(info.route) || [];\n existing.push(info);\n pathGroups.set(info.route, existing);\n }\n\n const pathEntries: string[] = [];\n\n for (const [route, infos] of pathGroups) {\n const methodEntries: string[] = [];\n\n for (const info of infos) {\n const methodDef = await this.generateMethodDefinition(info);\n methodEntries.push(` ${info.method.toLowerCase()}: ${methodDef};`);\n }\n\n // Add path parameters if present\n const firstWithParams = infos.find((i) => i.input?.params);\n let paramsEntry = '';\n if (firstWithParams?.input?.params) {\n const paramsName = await this.getSchemaName(\n firstWithParams.input.params,\n `${this.getSchemaBaseName(firstWithParams)}Params`,\n );\n paramsEntry = `\\n parameters: {\\n path: ${paramsName};\\n };`;\n }\n\n pathEntries.push(\n ` '${route}': {${paramsEntry}\\n${methodEntries.join('\\n')}\\n };`,\n );\n }\n\n return `export interface paths {\\n${pathEntries.join('\\n')}\\n}`;\n }\n\n private async generateMethodDefinition(info: EndpointInfo): Promise<string> {\n const parts: string[] = [];\n const baseName = this.getSchemaBaseName(info);\n\n // Request body\n if (info.input?.body) {\n const bodyName = await this.getSchemaName(\n info.input.body,\n `${baseName}Input`,\n );\n parts.push(`requestBody: {\n content: {\n 'application/json': ${bodyName};\n };\n }`);\n }\n\n // Query parameters\n if (info.input?.query) {\n const queryName = await this.getSchemaName(\n info.input.query,\n `${baseName}Query`,\n );\n parts.push(`parameters: {\n query: ${queryName};\n }`);\n }\n\n // Responses\n const outputName = info.output\n ? await this.getSchemaName(info.output, `${baseName}Output`)\n : 'unknown';\n parts.push(`responses: {\n 200: {\n content: {\n 'application/json': ${outputName};\n };\n };\n }`);\n\n return `{\\n ${parts.join(';\\n ')};\\n }`;\n }\n\n private buildModule(params: {\n title: string;\n version: string;\n description?: string;\n securitySchemes: SecuritySchemeInfo[];\n endpointAuth: Record<string, string | null>;\n schemaInterfaces: string;\n pathsInterface: string;\n }): string {\n const {\n title,\n version,\n description,\n securitySchemes,\n endpointAuth,\n schemaInterfaces,\n pathsInterface,\n } = params;\n\n const securitySchemesObj = securitySchemes.reduce(\n (acc, s) => {\n acc[s.name] = s.scheme;\n return acc;\n },\n {} as Record<string, SecuritySchemeObject>,\n );\n\n const schemeNames = securitySchemes.map((s) => `'${s.name}'`).join(' | ');\n\n // Generate createApi only if there are security schemes\n const hasSecuritySchemes = schemeNames.length > 0;\n\n const createApiSection = hasSecuritySchemes\n ? `\n// ============================================================\n// API Client Factory\n// ============================================================\n\nimport {\n createAuthAwareFetcher,\n type AuthStrategy,\n} from '@geekmidas/client/auth-fetcher';\nimport { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';\nimport type { QueryClient } from '@tanstack/react-query';\n\n/**\n * Options for creating the API client.\n */\nexport interface CreateApiOptions {\n /** Base URL for all API requests (required) */\n baseURL: string;\n /** Auth strategies for each security scheme used in this API */\n authStrategies: Record<SecuritySchemeId, AuthStrategy>;\n /** Optional React Query client instance */\n queryClient?: QueryClient;\n /** Optional request interceptor */\n onRequest?: (config: RequestInit) => RequestInit | Promise<RequestInit>;\n}\n\n/**\n * Create a type-safe API client with authentication and React Query hooks.\n *\n * @example\n * \\`\\`\\`typescript\n * const api = createApi({\n * baseURL: 'https://api.example.com',\n * authStrategies: {\n * jwt: { type: 'bearer', tokenProvider },\n * },\n * });\n *\n * // Imperative fetch\n * const user = await api('GET /users/{id}', { params: { id: '123' } });\n *\n * // React Query hooks\n * const { data } = api.useQuery('GET /users/{id}', { params: { id: '123' } });\n * const mutation = api.useMutation('POST /users');\n * \\`\\`\\`\n */\nexport function createApi(options: CreateApiOptions) {\n const fetcher = createAuthAwareFetcher<paths, typeof endpointAuth, typeof securitySchemes>({\n baseURL: options.baseURL,\n endpointAuth,\n securitySchemes,\n authStrategies: options.authStrategies,\n onRequest: options.onRequest,\n });\n\n const hooks = createEndpointHooks<paths>(fetcher, { queryClient: options.queryClient });\n\n return Object.assign(fetcher, hooks);\n}\n`\n : `\n// ============================================================\n// API Client Factory\n// ============================================================\n\nimport { TypedFetcher, type FetcherOptions } from '@geekmidas/client/fetcher';\nimport { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';\nimport type { QueryClient } from '@tanstack/react-query';\n\n/**\n * Options for creating the API client.\n */\nexport interface CreateApiOptions extends Omit<FetcherOptions, 'baseURL'> {\n /** Base URL for all API requests (required) */\n baseURL: string;\n /** Optional React Query client instance */\n queryClient?: QueryClient;\n}\n\n/**\n * Create a type-safe API client with React Query hooks.\n *\n * @example\n * \\`\\`\\`typescript\n * const api = createApi({\n * baseURL: 'https://api.example.com',\n * });\n *\n * // Imperative fetch\n * const data = await api('GET /health');\n *\n * // React Query hooks\n * const { data } = api.useQuery('GET /health');\n * \\`\\`\\`\n */\nexport function createApi(options: CreateApiOptions) {\n const { queryClient, ...fetcherOptions } = options;\n const fetcher = new TypedFetcher<paths>(fetcherOptions);\n\n const hooks = createEndpointHooks<paths>(fetcher.request.bind(fetcher), { queryClient });\n\n return Object.assign(fetcher.request.bind(fetcher), hooks);\n}\n`;\n\n return `// Auto-generated by @geekmidas/cli - DO NOT EDIT\n// Generated: ${new Date().toISOString()}\n\n// ============================================================\n// Security Scheme Type\n// ============================================================\n\ninterface SecuritySchemeObject {\n type: 'apiKey' | 'http' | 'mutualTLS' | 'oauth2' | 'openIdConnect';\n description?: string;\n name?: string;\n in?: 'query' | 'header' | 'cookie';\n scheme?: string;\n bearerFormat?: string;\n flows?: Record<string, unknown>;\n openIdConnectUrl?: string;\n [key: string]: unknown;\n}\n\n// ============================================================\n// API Info\n// ============================================================\n\nexport const apiInfo = {\n title: '${title}',\n version: '${version}',${description ? `\\n description: '${description.replace(/'/g, \"\\\\'\")}',` : ''}\n} as const;\n\n// ============================================================\n// Security Schemes\n// ============================================================\n\n/**\n * Available security schemes for this API.\n * Maps authorizer names to OpenAPI security scheme definitions.\n */\nexport const securitySchemes = ${JSON.stringify(securitySchemesObj, null, 2).replace(/\"([^\"]+)\":/g, '$1:')} as const satisfies Record<string, SecuritySchemeObject>;\n\nexport type SecuritySchemeId = ${schemeNames || 'never'};\n\n// ============================================================\n// Endpoint Authentication Map\n// ============================================================\n\n/**\n * Runtime map of endpoints to their required authentication scheme.\n * \\`null\\` indicates a public endpoint (no auth required).\n */\nexport const endpointAuth = ${JSON.stringify(endpointAuth, null, 2).replace(/\"([^\"]+)\":/g, \"'$1':\")} as const satisfies Record<string, SecuritySchemeId | null>;\n\nexport type EndpointString = keyof typeof endpointAuth;\n\nexport type AuthenticatedEndpoint = {\n [K in EndpointString]: typeof endpointAuth[K] extends null ? never : K;\n}[EndpointString];\n\nexport type PublicEndpoint = {\n [K in EndpointString]: typeof endpointAuth[K] extends null ? K : never;\n}[EndpointString];\n\n// ============================================================\n// Schema Definitions\n// ============================================================\n\n${schemaInterfaces}\n\n// ============================================================\n// OpenAPI Paths\n// ============================================================\n\n${pathsInterface}\n${createApiSection}\n`;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OpenApiTsGenerator-q3aWNkuM.mjs","names":["endpoints: Endpoint<any, any, any, any, any, any>[]","options: OpenApiTsOptions","endpointInfos: EndpointInfo[]","type: string","_name: string","authMap: Record<string, string | null>","interfaces: string[]","schema: StandardSchemaV1","defaultName: string","info: EndpointInfo","str: string","name: string","collectedDefs: Map<string, JsonSchema>","schema: JsonSchema","props: string[]","s: JsonSchema","e: string","pathEntries: string[]","methodEntries: string[]","parts: string[]","params: {\n title: string;\n version: string;\n description?: string;\n securitySchemes: SecuritySchemeInfo[];\n endpointAuth: Record<string, string | null>;\n schemaInterfaces: string;\n pathsInterface: string;\n }"],"sources":["../src/generators/OpenApiTsGenerator.ts"],"sourcesContent":["import type { Endpoint } from '@geekmidas/constructs/endpoints';\nimport {\n StandardSchemaJsonSchema,\n getSchemaMetadata,\n} from '@geekmidas/schema/conversion';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\n\ninterface OpenApiTsOptions {\n title?: string;\n version?: string;\n description?: string;\n}\n\n// JSON Schema type definition\ninterface JsonSchema {\n type?: string;\n properties?: Record<string, JsonSchema>;\n items?: JsonSchema;\n required?: string[];\n enum?: string[];\n $ref?: string;\n anyOf?: JsonSchema[];\n oneOf?: JsonSchema[];\n allOf?: JsonSchema[];\n additionalProperties?: boolean | JsonSchema;\n [key: string]: unknown;\n}\n\n// Security scheme type (OpenAPI 3.1)\ninterface SecuritySchemeObject {\n type: 'apiKey' | 'http' | 'mutualTLS' | 'oauth2' | 'openIdConnect';\n description?: string;\n name?: string;\n in?: 'query' | 'header' | 'cookie';\n scheme?: string;\n bearerFormat?: string;\n flows?: Record<string, unknown>;\n openIdConnectUrl?: string;\n [key: string]: unknown;\n}\n\ninterface EndpointInfo {\n endpoint: string;\n route: string;\n method: string;\n authorizerName: string | null;\n /** @deprecated Use securityScheme instead */\n authorizerType: string | null;\n /** The OpenAPI security scheme definition for this endpoint's authorizer */\n securityScheme: SecuritySchemeObject | null;\n input?: {\n body?: StandardSchemaV1;\n query?: StandardSchemaV1;\n params?: StandardSchemaV1;\n };\n output?: StandardSchemaV1;\n description?: string;\n tags?: string[];\n operationId?: string;\n}\n\ninterface SecuritySchemeInfo {\n name: string;\n type: string;\n scheme: SecuritySchemeObject;\n}\n\n/**\n * Generates TypeScript OpenAPI module from endpoints.\n * Outputs:\n * - securitySchemes: typed security scheme definitions\n * - endpointAuth: runtime map of endpoints to auth requirements\n * - paths: TypeScript interface for type-safe fetcher\n * - schema interfaces: reusable TypeScript types from Zod/Valibot schemas\n */\nexport class OpenApiTsGenerator {\n async generate(\n endpoints: Endpoint<any, any, any, any, any, any>[],\n options: OpenApiTsOptions = {},\n ): Promise<string> {\n const { title = 'API', version = '1.0.0', description } = options;\n\n // Extract endpoint info\n const endpointInfos = await this.extractEndpointInfos(endpoints);\n\n // Collect unique security schemes\n const securitySchemes = this.collectSecuritySchemes(endpointInfos);\n\n // Build endpoint auth map\n const endpointAuth = this.buildEndpointAuthMap(endpointInfos);\n\n // Generate schema interfaces\n const schemaInterfaces = await this.generateSchemaInterfaces(endpointInfos);\n\n // Generate paths interface\n const pathsInterface = await this.generatePathsInterface(endpointInfos);\n\n // Build the final TypeScript module\n return this.buildModule({\n title,\n version,\n description,\n securitySchemes,\n endpointAuth,\n schemaInterfaces,\n pathsInterface,\n });\n }\n\n private async extractEndpointInfos(\n endpoints: Endpoint<any, any, any, any, any, any>[],\n ): Promise<EndpointInfo[]> {\n return endpoints.map((ep) => {\n const route = ep.route.replace(/:(\\w+)/g, '{$1}');\n const method = ep.method.toUpperCase();\n\n // Get security scheme from authorizer (if available)\n // This is the preferred way - the scheme is stored directly on the authorizer\n const securityScheme = ep.authorizer?.securityScheme as\n | SecuritySchemeObject\n | undefined;\n\n return {\n endpoint: `${method} ${route}`,\n route,\n method,\n authorizerName: ep.authorizer?.name ?? null,\n authorizerType: ep.authorizer?.type ?? null,\n securityScheme: securityScheme ?? null,\n input: ep.input,\n output: ep.outputSchema,\n description: ep.description,\n tags: ep.tags,\n operationId: ep.operationId,\n };\n });\n }\n\n private collectSecuritySchemes(\n endpointInfos: EndpointInfo[],\n ): SecuritySchemeInfo[] {\n const schemes = new Map<string, SecuritySchemeInfo>();\n\n for (const info of endpointInfos) {\n if (info.authorizerName && !schemes.has(info.authorizerName)) {\n // Prefer the stored security scheme (from .securitySchemes() or built-ins)\n // Fall back to inference from authorizerType for backward compatibility\n const scheme =\n info.securityScheme ??\n (info.authorizerType\n ? this.mapAuthorizerToSecurityScheme(\n info.authorizerType,\n info.authorizerName,\n )\n : null);\n\n if (scheme) {\n schemes.set(info.authorizerName, {\n name: info.authorizerName,\n type: scheme.type,\n scheme,\n });\n }\n }\n }\n\n return Array.from(schemes.values());\n }\n\n private mapAuthorizerToSecurityScheme(\n type: string,\n _name: string,\n ): SecuritySchemeObject {\n switch (type.toLowerCase()) {\n case 'jwt':\n case 'bearer':\n return {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n };\n case 'iam':\n case 'aws-sigv4':\n case 'sigv4':\n return {\n type: 'apiKey',\n in: 'header',\n name: 'Authorization',\n 'x-amazon-apigateway-authtype': 'awsSigv4',\n };\n case 'apikey':\n case 'api-key':\n return {\n type: 'apiKey',\n in: 'header',\n name: 'X-API-Key',\n };\n case 'oauth2':\n return {\n type: 'oauth2',\n flows: {},\n };\n case 'oidc':\n case 'openidconnect':\n return {\n type: 'openIdConnect',\n openIdConnectUrl: '',\n };\n default:\n return {\n type: 'http',\n scheme: 'bearer',\n };\n }\n }\n\n private buildEndpointAuthMap(\n endpointInfos: EndpointInfo[],\n ): Record<string, string | null> {\n const authMap: Record<string, string | null> = {};\n\n for (const info of endpointInfos) {\n authMap[info.endpoint] = info.authorizerName;\n }\n\n return authMap;\n }\n\n private async generateSchemaInterfaces(\n endpointInfos: EndpointInfo[],\n ): Promise<string> {\n const interfaces: string[] = [];\n const generatedNames = new Set<string>();\n // Collect nested schemas with $defs (from .meta({ id: 'X' }))\n const collectedDefs = new Map<string, JsonSchema>();\n\n for (const info of endpointInfos) {\n const baseName = this.getSchemaBaseName(info);\n\n // Input body schema\n if (info.input?.body) {\n const name = await this.getSchemaName(\n info.input.body,\n `${baseName}Input`,\n );\n if (!generatedNames.has(name)) {\n const schema = await this.schemaToInterfaceWithDefs(\n info.input.body,\n name,\n collectedDefs,\n );\n if (schema) {\n interfaces.push(schema);\n generatedNames.add(name);\n }\n }\n }\n\n // Input params schema\n if (info.input?.params) {\n const name = await this.getSchemaName(\n info.input.params,\n `${baseName}Params`,\n );\n if (!generatedNames.has(name)) {\n const schema = await this.schemaToInterfaceWithDefs(\n info.input.params,\n name,\n collectedDefs,\n );\n if (schema) {\n interfaces.push(schema);\n generatedNames.add(name);\n }\n }\n }\n\n // Input query schema\n if (info.input?.query) {\n const name = await this.getSchemaName(\n info.input.query,\n `${baseName}Query`,\n );\n if (!generatedNames.has(name)) {\n const schema = await this.schemaToInterfaceWithDefs(\n info.input.query,\n name,\n collectedDefs,\n );\n if (schema) {\n interfaces.push(schema);\n generatedNames.add(name);\n }\n }\n }\n\n // Output schema\n if (info.output) {\n const name = await this.getSchemaName(info.output, `${baseName}Output`);\n if (!generatedNames.has(name)) {\n const schema = await this.schemaToInterfaceWithDefs(\n info.output,\n name,\n collectedDefs,\n );\n if (schema) {\n interfaces.push(schema);\n generatedNames.add(name);\n }\n }\n }\n }\n\n // Generate interfaces for collected $defs (nested schemas with .meta({ id: 'X' }))\n for (const [defName, defSchema] of collectedDefs) {\n if (!generatedNames.has(defName)) {\n const interfaceStr = this.jsonSchemaToInterface(defSchema, defName);\n interfaces.push(interfaceStr);\n generatedNames.add(defName);\n }\n }\n\n return interfaces.join('\\n\\n');\n }\n\n /**\n * Get the name for a schema, using metadata `id` if available,\n * otherwise falling back to the provided default name.\n */\n private async getSchemaName(\n schema: StandardSchemaV1,\n defaultName: string,\n ): Promise<string> {\n try {\n const metadata = await getSchemaMetadata(schema);\n if (metadata?.id) {\n return this.pascalCase(metadata.id);\n }\n } catch {\n // Ignore metadata extraction errors\n }\n return defaultName;\n }\n\n private getSchemaBaseName(info: EndpointInfo): string {\n if (info.operationId) {\n return this.pascalCase(info.operationId);\n }\n\n // Generate name from method + route\n const routeParts = info.route\n .replace(/[{}]/g, '')\n .split('/')\n .filter(Boolean)\n .map((part) => this.pascalCase(part));\n\n return `${this.pascalCase(info.method.toLowerCase())}${routeParts.join('')}`;\n }\n\n private pascalCase(str: string): string {\n return str\n .replace(/[-_](.)/g, (_, c) => c.toUpperCase())\n .replace(/^./, (c) => c.toUpperCase());\n }\n\n /**\n * Convert schema to interface while collecting $defs for nested schemas\n * with .meta({ id: 'X' }).\n */\n private async schemaToInterfaceWithDefs(\n schema: StandardSchemaV1,\n name: string,\n collectedDefs: Map<string, JsonSchema>,\n ): Promise<string | null> {\n try {\n // Get raw JSON schema with $defs intact (don't use convertStandardSchemaToJsonSchema\n // which strips $defs)\n const vendor = schema['~standard']?.vendor;\n if (!vendor || !(vendor in StandardSchemaJsonSchema)) {\n return null;\n }\n\n const toJsonSchema =\n StandardSchemaJsonSchema[\n vendor as keyof typeof StandardSchemaJsonSchema\n ];\n const jsonSchema = await toJsonSchema(schema);\n if (!jsonSchema) return null;\n\n // Extract $defs from the JSON schema (these come from .meta({ id: 'X' }))\n if (jsonSchema.$defs && typeof jsonSchema.$defs === 'object') {\n for (const [defName, defSchema] of Object.entries(jsonSchema.$defs)) {\n if (!collectedDefs.has(defName)) {\n // Remove the 'id' field from the schema as it's just metadata\n const { id, ...schemaWithoutId } = defSchema as JsonSchema & {\n id?: string;\n };\n collectedDefs.set(defName, schemaWithoutId as JsonSchema);\n }\n }\n }\n\n // Remove $defs from the schema before converting to interface\n const { $defs, ...schemaWithoutDefs } = jsonSchema;\n return this.jsonSchemaToInterface(schemaWithoutDefs, name);\n } catch {\n return null;\n }\n }\n\n private jsonSchemaToInterface(schema: JsonSchema, name: string): string {\n if (schema.type !== 'object' || !schema.properties) {\n // For non-object types, create a type alias\n const typeStr = this.jsonSchemaTypeToTs(schema);\n return `export type ${name} = ${typeStr};`;\n }\n\n const props: string[] = [];\n const required = new Set(schema.required || []);\n\n for (const [propName, propSchema] of Object.entries(schema.properties)) {\n const isRequired = required.has(propName);\n const typeStr = this.jsonSchemaTypeToTs(propSchema as JsonSchema);\n const optionalMark = isRequired ? '' : '?';\n props.push(` ${propName}${optionalMark}: ${typeStr};`);\n }\n\n return `export interface ${name} {\\n${props.join('\\n')}\\n}`;\n }\n\n private jsonSchemaTypeToTs(schema: JsonSchema): string {\n if (!schema) return 'unknown';\n\n if (schema.$ref) {\n // Extract name from $ref\n const refName = schema.$ref.split('/').pop() || 'unknown';\n return refName;\n }\n\n if (schema.anyOf) {\n return schema.anyOf\n .map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n .join(' | ');\n }\n\n if (schema.oneOf) {\n return schema.oneOf\n .map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n .join(' | ');\n }\n\n if (schema.allOf) {\n return schema.allOf\n .map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n .join(' & ');\n }\n\n switch (schema.type) {\n case 'string':\n if (schema.enum) {\n return schema.enum.map((e: string) => `'${e}'`).join(' | ');\n }\n return 'string';\n case 'number':\n case 'integer':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'null':\n return 'null';\n case 'array':\n if (schema.items) {\n return `Array<${this.jsonSchemaTypeToTs(schema.items as JsonSchema)}>`;\n }\n return 'Array<unknown>';\n case 'object':\n if (schema.properties) {\n const props: string[] = [];\n const required = new Set(schema.required || []);\n for (const [propName, propSchema] of Object.entries(\n schema.properties,\n )) {\n const isRequired = required.has(propName);\n const typeStr = this.jsonSchemaTypeToTs(propSchema as JsonSchema);\n const optionalMark = isRequired ? '' : '?';\n props.push(`${propName}${optionalMark}: ${typeStr}`);\n }\n return `{ ${props.join('; ')} }`;\n }\n if (schema.additionalProperties) {\n const valueType = this.jsonSchemaTypeToTs(\n schema.additionalProperties as JsonSchema,\n );\n return `Record<string, ${valueType}>`;\n }\n return 'Record<string, unknown>';\n default:\n return 'unknown';\n }\n }\n\n private async generatePathsInterface(\n endpointInfos: EndpointInfo[],\n ): Promise<string> {\n const pathGroups = new Map<string, EndpointInfo[]>();\n\n // Group endpoints by route\n for (const info of endpointInfos) {\n const existing = pathGroups.get(info.route) || [];\n existing.push(info);\n pathGroups.set(info.route, existing);\n }\n\n const pathEntries: string[] = [];\n\n for (const [route, infos] of pathGroups) {\n const methodEntries: string[] = [];\n\n for (const info of infos) {\n const methodDef = await this.generateMethodDefinition(info);\n methodEntries.push(` ${info.method.toLowerCase()}: ${methodDef};`);\n }\n\n // Add path parameters if present\n const firstWithParams = infos.find((i) => i.input?.params);\n let paramsEntry = '';\n if (firstWithParams?.input?.params) {\n const paramsName = await this.getSchemaName(\n firstWithParams.input.params,\n `${this.getSchemaBaseName(firstWithParams)}Params`,\n );\n paramsEntry = `\\n parameters: {\\n path: ${paramsName};\\n };`;\n }\n\n pathEntries.push(\n ` '${route}': {${paramsEntry}\\n${methodEntries.join('\\n')}\\n };`,\n );\n }\n\n return `export interface paths {\\n${pathEntries.join('\\n')}\\n}`;\n }\n\n private async generateMethodDefinition(info: EndpointInfo): Promise<string> {\n const parts: string[] = [];\n const baseName = this.getSchemaBaseName(info);\n\n // Request body\n if (info.input?.body) {\n const bodyName = await this.getSchemaName(\n info.input.body,\n `${baseName}Input`,\n );\n parts.push(`requestBody: {\n content: {\n 'application/json': ${bodyName};\n };\n }`);\n }\n\n // Query parameters\n if (info.input?.query) {\n const queryName = await this.getSchemaName(\n info.input.query,\n `${baseName}Query`,\n );\n parts.push(`parameters: {\n query: ${queryName};\n }`);\n }\n\n // Responses\n const outputName = info.output\n ? await this.getSchemaName(info.output, `${baseName}Output`)\n : 'unknown';\n parts.push(`responses: {\n 200: {\n content: {\n 'application/json': ${outputName};\n };\n };\n }`);\n\n return `{\\n ${parts.join(';\\n ')};\\n }`;\n }\n\n private buildModule(params: {\n title: string;\n version: string;\n description?: string;\n securitySchemes: SecuritySchemeInfo[];\n endpointAuth: Record<string, string | null>;\n schemaInterfaces: string;\n pathsInterface: string;\n }): string {\n const {\n title,\n version,\n description,\n securitySchemes,\n endpointAuth,\n schemaInterfaces,\n pathsInterface,\n } = params;\n\n const securitySchemesObj = securitySchemes.reduce(\n (acc, s) => {\n acc[s.name] = s.scheme;\n return acc;\n },\n {} as Record<string, SecuritySchemeObject>,\n );\n\n const schemeNames = securitySchemes.map((s) => `'${s.name}'`).join(' | ');\n\n // Generate createApi only if there are security schemes\n const hasSecuritySchemes = schemeNames.length > 0;\n\n const createApiSection = hasSecuritySchemes\n ? `\n// ============================================================\n// API Client Factory\n// ============================================================\n\nimport {\n createAuthAwareFetcher,\n type AuthStrategy,\n} from '@geekmidas/client/auth-fetcher';\nimport { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';\nimport type { QueryClient } from '@tanstack/react-query';\n\n/**\n * Options for creating the API client.\n */\nexport interface CreateApiOptions {\n /** Base URL for all API requests (required) */\n baseURL: string;\n /** Auth strategies for each security scheme used in this API */\n authStrategies: Record<SecuritySchemeId, AuthStrategy>;\n /** Optional React Query client instance */\n queryClient?: QueryClient;\n /** Optional request interceptor */\n onRequest?: (config: RequestInit) => RequestInit | Promise<RequestInit>;\n}\n\n/**\n * Create a type-safe API client with authentication and React Query hooks.\n *\n * @example\n * \\`\\`\\`typescript\n * const api = createApi({\n * baseURL: 'https://api.example.com',\n * authStrategies: {\n * jwt: { type: 'bearer', tokenProvider },\n * },\n * });\n *\n * // Imperative fetch\n * const user = await api('GET /users/{id}', { params: { id: '123' } });\n *\n * // React Query hooks\n * const { data } = api.useQuery('GET /users/{id}', { params: { id: '123' } });\n * const mutation = api.useMutation('POST /users');\n * \\`\\`\\`\n */\nexport function createApi(options: CreateApiOptions) {\n const fetcher = createAuthAwareFetcher<paths, typeof endpointAuth, typeof securitySchemes>({\n baseURL: options.baseURL,\n endpointAuth,\n securitySchemes,\n authStrategies: options.authStrategies,\n onRequest: options.onRequest,\n });\n\n const hooks = createEndpointHooks<paths>(fetcher, { queryClient: options.queryClient });\n\n return Object.assign(fetcher, hooks);\n}\n`\n : `\n// ============================================================\n// API Client Factory\n// ============================================================\n\nimport { TypedFetcher, type FetcherOptions } from '@geekmidas/client/fetcher';\nimport { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';\nimport type { QueryClient } from '@tanstack/react-query';\n\n/**\n * Options for creating the API client.\n */\nexport interface CreateApiOptions extends Omit<FetcherOptions, 'baseURL'> {\n /** Base URL for all API requests (required) */\n baseURL: string;\n /** Optional React Query client instance */\n queryClient?: QueryClient;\n}\n\n/**\n * Create a type-safe API client with React Query hooks.\n *\n * @example\n * \\`\\`\\`typescript\n * const api = createApi({\n * baseURL: 'https://api.example.com',\n * });\n *\n * // Imperative fetch\n * const data = await api('GET /health');\n *\n * // React Query hooks\n * const { data } = api.useQuery('GET /health');\n * \\`\\`\\`\n */\nexport function createApi(options: CreateApiOptions) {\n const { queryClient, ...fetcherOptions } = options;\n const fetcher = new TypedFetcher<paths>(fetcherOptions);\n\n const hooks = createEndpointHooks<paths>(fetcher.request.bind(fetcher), { queryClient });\n\n return Object.assign(fetcher.request.bind(fetcher), hooks);\n}\n`;\n\n return `// Auto-generated by @geekmidas/cli - DO NOT EDIT\n// Generated: ${new Date().toISOString()}\n\n// ============================================================\n// Security Scheme Type\n// ============================================================\n\ninterface SecuritySchemeObject {\n type: 'apiKey' | 'http' | 'mutualTLS' | 'oauth2' | 'openIdConnect';\n description?: string;\n name?: string;\n in?: 'query' | 'header' | 'cookie';\n scheme?: string;\n bearerFormat?: string;\n flows?: Record<string, unknown>;\n openIdConnectUrl?: string;\n [key: string]: unknown;\n}\n\n// ============================================================\n// API Info\n// ============================================================\n\nexport const apiInfo = {\n title: '${title}',\n version: '${version}',${description ? `\\n description: '${description.replace(/'/g, \"\\\\'\")}',` : ''}\n} as const;\n\n// ============================================================\n// Security Schemes\n// ============================================================\n\n/**\n * Available security schemes for this API.\n * Maps authorizer names to OpenAPI security scheme definitions.\n */\nexport const securitySchemes = ${JSON.stringify(securitySchemesObj, null, 2).replace(/\"([^\"]+)\":/g, '$1:')} as const satisfies Record<string, SecuritySchemeObject>;\n\nexport type SecuritySchemeId = ${schemeNames || 'never'};\n\n// ============================================================\n// Endpoint Authentication Map\n// ============================================================\n\n/**\n * Runtime map of endpoints to their required authentication scheme.\n * \\`null\\` indicates a public endpoint (no auth required).\n */\nexport const endpointAuth = ${JSON.stringify(endpointAuth, null, 2).replace(/\"([^\"]+)\":/g, \"'$1':\")} as const satisfies Record<string, SecuritySchemeId | null>;\n\nexport type EndpointString = keyof typeof endpointAuth;\n\nexport type AuthenticatedEndpoint = {\n [K in EndpointString]: typeof endpointAuth[K] extends null ? never : K;\n}[EndpointString];\n\nexport type PublicEndpoint = {\n [K in EndpointString]: typeof endpointAuth[K] extends null ? K : never;\n}[EndpointString];\n\n// ============================================================\n// Schema Definitions\n// ============================================================\n\n${schemaInterfaces}\n\n// ============================================================\n// OpenAPI Paths\n// ============================================================\n\n${pathsInterface}\n${createApiSection}\n`;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OpenApiTsGenerator-C4mHHaku.mjs","names":["endpoints: Endpoint<any, any, any, any, any, any>[]","options: OpenApiTsOptions","endpointInfos: EndpointInfo[]","type: string","_name: string","authMap: Record<string, string | null>","interfaces: string[]","schema: StandardSchemaV1","defaultName: string","info: EndpointInfo","str: string","name: string","collectedDefs: Map<string, JsonSchema>","schema: JsonSchema","props: string[]","s: JsonSchema","e: string","pathEntries: string[]","methodEntries: string[]","parts: string[]","params: {\n title: string;\n version: string;\n description?: string;\n securitySchemes: SecuritySchemeInfo[];\n endpointAuth: Record<string, string | null>;\n schemaInterfaces: string;\n pathsInterface: string;\n }"],"sources":["../src/generators/OpenApiTsGenerator.ts"],"sourcesContent":["import type { Endpoint } from '@geekmidas/constructs/endpoints';\nimport {\n StandardSchemaJsonSchema,\n getSchemaMetadata,\n} from '@geekmidas/schema/conversion';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\n\ninterface OpenApiTsOptions {\n title?: string;\n version?: string;\n description?: string;\n}\n\n// JSON Schema type definition\ninterface JsonSchema {\n type?: string;\n properties?: Record<string, JsonSchema>;\n items?: JsonSchema;\n required?: string[];\n enum?: string[];\n $ref?: string;\n anyOf?: JsonSchema[];\n oneOf?: JsonSchema[];\n allOf?: JsonSchema[];\n additionalProperties?: boolean | JsonSchema;\n [key: string]: unknown;\n}\n\n// Security scheme type (OpenAPI 3.1)\ninterface SecuritySchemeObject {\n type: 'apiKey' | 'http' | 'mutualTLS' | 'oauth2' | 'openIdConnect';\n description?: string;\n name?: string;\n in?: 'query' | 'header' | 'cookie';\n scheme?: string;\n bearerFormat?: string;\n flows?: Record<string, unknown>;\n openIdConnectUrl?: string;\n [key: string]: unknown;\n}\n\ninterface EndpointInfo {\n endpoint: string;\n route: string;\n method: string;\n authorizerName: string | null;\n /** @deprecated Use securityScheme instead */\n authorizerType: string | null;\n /** The OpenAPI security scheme definition for this endpoint's authorizer */\n securityScheme: SecuritySchemeObject | null;\n input?: {\n body?: StandardSchemaV1;\n query?: StandardSchemaV1;\n params?: StandardSchemaV1;\n };\n output?: StandardSchemaV1;\n description?: string;\n tags?: string[];\n operationId?: string;\n}\n\ninterface SecuritySchemeInfo {\n name: string;\n type: string;\n scheme: SecuritySchemeObject;\n}\n\n/**\n * Generates TypeScript OpenAPI module from endpoints.\n * Outputs:\n * - securitySchemes: typed security scheme definitions\n * - endpointAuth: runtime map of endpoints to auth requirements\n * - paths: TypeScript interface for type-safe fetcher\n * - schema interfaces: reusable TypeScript types from Zod/Valibot schemas\n */\nexport class OpenApiTsGenerator {\n async generate(\n endpoints: Endpoint<any, any, any, any, any, any>[],\n options: OpenApiTsOptions = {},\n ): Promise<string> {\n const { title = 'API', version = '1.0.0', description } = options;\n\n // Extract endpoint info\n const endpointInfos = await this.extractEndpointInfos(endpoints);\n\n // Collect unique security schemes\n const securitySchemes = this.collectSecuritySchemes(endpointInfos);\n\n // Build endpoint auth map\n const endpointAuth = this.buildEndpointAuthMap(endpointInfos);\n\n // Generate schema interfaces\n const schemaInterfaces = await this.generateSchemaInterfaces(endpointInfos);\n\n // Generate paths interface\n const pathsInterface = await this.generatePathsInterface(endpointInfos);\n\n // Build the final TypeScript module\n return this.buildModule({\n title,\n version,\n description,\n securitySchemes,\n endpointAuth,\n schemaInterfaces,\n pathsInterface,\n });\n }\n\n private async extractEndpointInfos(\n endpoints: Endpoint<any, any, any, any, any, any>[],\n ): Promise<EndpointInfo[]> {\n return endpoints.map((ep) => {\n const route = ep.route.replace(/:(\\w+)/g, '{$1}');\n const method = ep.method.toUpperCase();\n\n // Get security scheme from authorizer (if available)\n // This is the preferred way - the scheme is stored directly on the authorizer\n const securityScheme = ep.authorizer?.securityScheme as\n | SecuritySchemeObject\n | undefined;\n\n return {\n endpoint: `${method} ${route}`,\n route,\n method,\n authorizerName: ep.authorizer?.name ?? null,\n authorizerType: ep.authorizer?.type ?? null,\n securityScheme: securityScheme ?? null,\n input: ep.input,\n output: ep.outputSchema,\n description: ep.description,\n tags: ep.tags,\n operationId: ep.operationId,\n };\n });\n }\n\n private collectSecuritySchemes(\n endpointInfos: EndpointInfo[],\n ): SecuritySchemeInfo[] {\n const schemes = new Map<string, SecuritySchemeInfo>();\n\n for (const info of endpointInfos) {\n if (info.authorizerName && !schemes.has(info.authorizerName)) {\n // Prefer the stored security scheme (from .securitySchemes() or built-ins)\n // Fall back to inference from authorizerType for backward compatibility\n const scheme =\n info.securityScheme ??\n (info.authorizerType\n ? this.mapAuthorizerToSecurityScheme(\n info.authorizerType,\n info.authorizerName,\n )\n : null);\n\n if (scheme) {\n schemes.set(info.authorizerName, {\n name: info.authorizerName,\n type: scheme.type,\n scheme,\n });\n }\n }\n }\n\n return Array.from(schemes.values());\n }\n\n private mapAuthorizerToSecurityScheme(\n type: string,\n _name: string,\n ): SecuritySchemeObject {\n switch (type.toLowerCase()) {\n case 'jwt':\n case 'bearer':\n return {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n };\n case 'iam':\n case 'aws-sigv4':\n case 'sigv4':\n return {\n type: 'apiKey',\n in: 'header',\n name: 'Authorization',\n 'x-amazon-apigateway-authtype': 'awsSigv4',\n };\n case 'apikey':\n case 'api-key':\n return {\n type: 'apiKey',\n in: 'header',\n name: 'X-API-Key',\n };\n case 'oauth2':\n return {\n type: 'oauth2',\n flows: {},\n };\n case 'oidc':\n case 'openidconnect':\n return {\n type: 'openIdConnect',\n openIdConnectUrl: '',\n };\n default:\n return {\n type: 'http',\n scheme: 'bearer',\n };\n }\n }\n\n private buildEndpointAuthMap(\n endpointInfos: EndpointInfo[],\n ): Record<string, string | null> {\n const authMap: Record<string, string | null> = {};\n\n for (const info of endpointInfos) {\n authMap[info.endpoint] = info.authorizerName;\n }\n\n return authMap;\n }\n\n private async generateSchemaInterfaces(\n endpointInfos: EndpointInfo[],\n ): Promise<string> {\n const interfaces: string[] = [];\n const generatedNames = new Set<string>();\n // Collect nested schemas with $defs (from .meta({ id: 'X' }))\n const collectedDefs = new Map<string, JsonSchema>();\n\n for (const info of endpointInfos) {\n const baseName = this.getSchemaBaseName(info);\n\n // Input body schema\n if (info.input?.body) {\n const name = await this.getSchemaName(\n info.input.body,\n `${baseName}Input`,\n );\n if (!generatedNames.has(name)) {\n const schema = await this.schemaToInterfaceWithDefs(\n info.input.body,\n name,\n collectedDefs,\n );\n if (schema) {\n interfaces.push(schema);\n generatedNames.add(name);\n }\n }\n }\n\n // Input params schema\n if (info.input?.params) {\n const name = await this.getSchemaName(\n info.input.params,\n `${baseName}Params`,\n );\n if (!generatedNames.has(name)) {\n const schema = await this.schemaToInterfaceWithDefs(\n info.input.params,\n name,\n collectedDefs,\n );\n if (schema) {\n interfaces.push(schema);\n generatedNames.add(name);\n }\n }\n }\n\n // Input query schema\n if (info.input?.query) {\n const name = await this.getSchemaName(\n info.input.query,\n `${baseName}Query`,\n );\n if (!generatedNames.has(name)) {\n const schema = await this.schemaToInterfaceWithDefs(\n info.input.query,\n name,\n collectedDefs,\n );\n if (schema) {\n interfaces.push(schema);\n generatedNames.add(name);\n }\n }\n }\n\n // Output schema\n if (info.output) {\n const name = await this.getSchemaName(info.output, `${baseName}Output`);\n if (!generatedNames.has(name)) {\n const schema = await this.schemaToInterfaceWithDefs(\n info.output,\n name,\n collectedDefs,\n );\n if (schema) {\n interfaces.push(schema);\n generatedNames.add(name);\n }\n }\n }\n }\n\n // Generate interfaces for collected $defs (nested schemas with .meta({ id: 'X' }))\n for (const [defName, defSchema] of collectedDefs) {\n if (!generatedNames.has(defName)) {\n const interfaceStr = this.jsonSchemaToInterface(defSchema, defName);\n interfaces.push(interfaceStr);\n generatedNames.add(defName);\n }\n }\n\n return interfaces.join('\\n\\n');\n }\n\n /**\n * Get the name for a schema, using metadata `id` if available,\n * otherwise falling back to the provided default name.\n */\n private async getSchemaName(\n schema: StandardSchemaV1,\n defaultName: string,\n ): Promise<string> {\n try {\n const metadata = await getSchemaMetadata(schema);\n if (metadata?.id) {\n return this.pascalCase(metadata.id);\n }\n } catch {\n // Ignore metadata extraction errors\n }\n return defaultName;\n }\n\n private getSchemaBaseName(info: EndpointInfo): string {\n if (info.operationId) {\n return this.pascalCase(info.operationId);\n }\n\n // Generate name from method + route\n const routeParts = info.route\n .replace(/[{}]/g, '')\n .split('/')\n .filter(Boolean)\n .map((part) => this.pascalCase(part));\n\n return `${this.pascalCase(info.method.toLowerCase())}${routeParts.join('')}`;\n }\n\n private pascalCase(str: string): string {\n return str\n .replace(/[-_](.)/g, (_, c) => c.toUpperCase())\n .replace(/^./, (c) => c.toUpperCase());\n }\n\n /**\n * Convert schema to interface while collecting $defs for nested schemas\n * with .meta({ id: 'X' }).\n */\n private async schemaToInterfaceWithDefs(\n schema: StandardSchemaV1,\n name: string,\n collectedDefs: Map<string, JsonSchema>,\n ): Promise<string | null> {\n try {\n // Get raw JSON schema with $defs intact (don't use convertStandardSchemaToJsonSchema\n // which strips $defs)\n const vendor = schema['~standard']?.vendor;\n if (!vendor || !(vendor in StandardSchemaJsonSchema)) {\n return null;\n }\n\n const toJsonSchema =\n StandardSchemaJsonSchema[\n vendor as keyof typeof StandardSchemaJsonSchema\n ];\n const jsonSchema = await toJsonSchema(schema);\n if (!jsonSchema) return null;\n\n // Extract $defs from the JSON schema (these come from .meta({ id: 'X' }))\n if (jsonSchema.$defs && typeof jsonSchema.$defs === 'object') {\n for (const [defName, defSchema] of Object.entries(jsonSchema.$defs)) {\n if (!collectedDefs.has(defName)) {\n // Remove the 'id' field from the schema as it's just metadata\n const { id, ...schemaWithoutId } = defSchema as JsonSchema & {\n id?: string;\n };\n collectedDefs.set(defName, schemaWithoutId as JsonSchema);\n }\n }\n }\n\n // Remove $defs from the schema before converting to interface\n const { $defs, ...schemaWithoutDefs } = jsonSchema;\n return this.jsonSchemaToInterface(schemaWithoutDefs, name);\n } catch {\n return null;\n }\n }\n\n private jsonSchemaToInterface(schema: JsonSchema, name: string): string {\n if (schema.type !== 'object' || !schema.properties) {\n // For non-object types, create a type alias\n const typeStr = this.jsonSchemaTypeToTs(schema);\n return `export type ${name} = ${typeStr};`;\n }\n\n const props: string[] = [];\n const required = new Set(schema.required || []);\n\n for (const [propName, propSchema] of Object.entries(schema.properties)) {\n const isRequired = required.has(propName);\n const typeStr = this.jsonSchemaTypeToTs(propSchema as JsonSchema);\n const optionalMark = isRequired ? '' : '?';\n props.push(` ${propName}${optionalMark}: ${typeStr};`);\n }\n\n return `export interface ${name} {\\n${props.join('\\n')}\\n}`;\n }\n\n private jsonSchemaTypeToTs(schema: JsonSchema): string {\n if (!schema) return 'unknown';\n\n if (schema.$ref) {\n // Extract name from $ref\n const refName = schema.$ref.split('/').pop() || 'unknown';\n return refName;\n }\n\n if (schema.anyOf) {\n return schema.anyOf\n .map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n .join(' | ');\n }\n\n if (schema.oneOf) {\n return schema.oneOf\n .map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n .join(' | ');\n }\n\n if (schema.allOf) {\n return schema.allOf\n .map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n .join(' & ');\n }\n\n switch (schema.type) {\n case 'string':\n if (schema.enum) {\n return schema.enum.map((e: string) => `'${e}'`).join(' | ');\n }\n return 'string';\n case 'number':\n case 'integer':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'null':\n return 'null';\n case 'array':\n if (schema.items) {\n return `Array<${this.jsonSchemaTypeToTs(schema.items as JsonSchema)}>`;\n }\n return 'Array<unknown>';\n case 'object':\n if (schema.properties) {\n const props: string[] = [];\n const required = new Set(schema.required || []);\n for (const [propName, propSchema] of Object.entries(\n schema.properties,\n )) {\n const isRequired = required.has(propName);\n const typeStr = this.jsonSchemaTypeToTs(propSchema as JsonSchema);\n const optionalMark = isRequired ? '' : '?';\n props.push(`${propName}${optionalMark}: ${typeStr}`);\n }\n return `{ ${props.join('; ')} }`;\n }\n if (schema.additionalProperties) {\n const valueType = this.jsonSchemaTypeToTs(\n schema.additionalProperties as JsonSchema,\n );\n return `Record<string, ${valueType}>`;\n }\n return 'Record<string, unknown>';\n default:\n return 'unknown';\n }\n }\n\n private async generatePathsInterface(\n endpointInfos: EndpointInfo[],\n ): Promise<string> {\n const pathGroups = new Map<string, EndpointInfo[]>();\n\n // Group endpoints by route\n for (const info of endpointInfos) {\n const existing = pathGroups.get(info.route) || [];\n existing.push(info);\n pathGroups.set(info.route, existing);\n }\n\n const pathEntries: string[] = [];\n\n for (const [route, infos] of pathGroups) {\n const methodEntries: string[] = [];\n\n for (const info of infos) {\n const methodDef = await this.generateMethodDefinition(info);\n methodEntries.push(` ${info.method.toLowerCase()}: ${methodDef};`);\n }\n\n // Add path parameters if present\n const firstWithParams = infos.find((i) => i.input?.params);\n let paramsEntry = '';\n if (firstWithParams?.input?.params) {\n const paramsName = await this.getSchemaName(\n firstWithParams.input.params,\n `${this.getSchemaBaseName(firstWithParams)}Params`,\n );\n paramsEntry = `\\n parameters: {\\n path: ${paramsName};\\n };`;\n }\n\n pathEntries.push(\n ` '${route}': {${paramsEntry}\\n${methodEntries.join('\\n')}\\n };`,\n );\n }\n\n return `export interface paths {\\n${pathEntries.join('\\n')}\\n}`;\n }\n\n private async generateMethodDefinition(info: EndpointInfo): Promise<string> {\n const parts: string[] = [];\n const baseName = this.getSchemaBaseName(info);\n\n // Request body\n if (info.input?.body) {\n const bodyName = await this.getSchemaName(\n info.input.body,\n `${baseName}Input`,\n );\n parts.push(`requestBody: {\n content: {\n 'application/json': ${bodyName};\n };\n }`);\n }\n\n // Query parameters\n if (info.input?.query) {\n const queryName = await this.getSchemaName(\n info.input.query,\n `${baseName}Query`,\n );\n parts.push(`parameters: {\n query: ${queryName};\n }`);\n }\n\n // Responses\n const outputName = info.output\n ? await this.getSchemaName(info.output, `${baseName}Output`)\n : 'unknown';\n parts.push(`responses: {\n 200: {\n content: {\n 'application/json': ${outputName};\n };\n };\n }`);\n\n return `{\\n ${parts.join(';\\n ')};\\n }`;\n }\n\n private buildModule(params: {\n title: string;\n version: string;\n description?: string;\n securitySchemes: SecuritySchemeInfo[];\n endpointAuth: Record<string, string | null>;\n schemaInterfaces: string;\n pathsInterface: string;\n }): string {\n const {\n title,\n version,\n description,\n securitySchemes,\n endpointAuth,\n schemaInterfaces,\n pathsInterface,\n } = params;\n\n const securitySchemesObj = securitySchemes.reduce(\n (acc, s) => {\n acc[s.name] = s.scheme;\n return acc;\n },\n {} as Record<string, SecuritySchemeObject>,\n );\n\n const schemeNames = securitySchemes.map((s) => `'${s.name}'`).join(' | ');\n\n // Generate createApi only if there are security schemes\n const hasSecuritySchemes = schemeNames.length > 0;\n\n const createApiSection = hasSecuritySchemes\n ? `\n// ============================================================\n// API Client Factory\n// ============================================================\n\nimport {\n createAuthAwareFetcher,\n type AuthStrategy,\n} from '@geekmidas/client/auth-fetcher';\nimport { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';\nimport type { QueryClient } from '@tanstack/react-query';\n\n/**\n * Options for creating the API client.\n */\nexport interface CreateApiOptions {\n /** Base URL for all API requests (required) */\n baseURL: string;\n /** Auth strategies for each security scheme used in this API */\n authStrategies: Record<SecuritySchemeId, AuthStrategy>;\n /** Optional React Query client instance */\n queryClient?: QueryClient;\n /** Optional request interceptor */\n onRequest?: (config: RequestInit) => RequestInit | Promise<RequestInit>;\n}\n\n/**\n * Create a type-safe API client with authentication and React Query hooks.\n *\n * @example\n * \\`\\`\\`typescript\n * const api = createApi({\n * baseURL: 'https://api.example.com',\n * authStrategies: {\n * jwt: { type: 'bearer', tokenProvider },\n * },\n * });\n *\n * // Imperative fetch\n * const user = await api('GET /users/{id}', { params: { id: '123' } });\n *\n * // React Query hooks\n * const { data } = api.useQuery('GET /users/{id}', { params: { id: '123' } });\n * const mutation = api.useMutation('POST /users');\n * \\`\\`\\`\n */\nexport function createApi(options: CreateApiOptions) {\n const fetcher = createAuthAwareFetcher<paths, typeof endpointAuth, typeof securitySchemes>({\n baseURL: options.baseURL,\n endpointAuth,\n securitySchemes,\n authStrategies: options.authStrategies,\n onRequest: options.onRequest,\n });\n\n const hooks = createEndpointHooks<paths>(fetcher, { queryClient: options.queryClient });\n\n return Object.assign(fetcher, hooks);\n}\n`\n : `\n// ============================================================\n// API Client Factory\n// ============================================================\n\nimport { TypedFetcher, type FetcherOptions } from '@geekmidas/client/fetcher';\nimport { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';\nimport type { QueryClient } from '@tanstack/react-query';\n\n/**\n * Options for creating the API client.\n */\nexport interface CreateApiOptions extends Omit<FetcherOptions, 'baseURL'> {\n /** Base URL for all API requests (required) */\n baseURL: string;\n /** Optional React Query client instance */\n queryClient?: QueryClient;\n}\n\n/**\n * Create a type-safe API client with React Query hooks.\n *\n * @example\n * \\`\\`\\`typescript\n * const api = createApi({\n * baseURL: 'https://api.example.com',\n * });\n *\n * // Imperative fetch\n * const data = await api('GET /health');\n *\n * // React Query hooks\n * const { data } = api.useQuery('GET /health');\n * \\`\\`\\`\n */\nexport function createApi(options: CreateApiOptions) {\n const { queryClient, ...fetcherOptions } = options;\n const fetcher = new TypedFetcher<paths>(fetcherOptions);\n\n const hooks = createEndpointHooks<paths>(fetcher.request.bind(fetcher), { queryClient });\n\n return Object.assign(fetcher.request.bind(fetcher), hooks);\n}\n`;\n\n return `// Auto-generated by @geekmidas/cli - DO NOT EDIT\n// Generated: ${new Date().toISOString()}\n\n// ============================================================\n// Security Scheme Type\n// ============================================================\n\ninterface SecuritySchemeObject {\n type: 'apiKey' | 'http' | 'mutualTLS' | 'oauth2' | 'openIdConnect';\n description?: string;\n name?: string;\n in?: 'query' | 'header' | 'cookie';\n scheme?: string;\n bearerFormat?: string;\n flows?: Record<string, unknown>;\n openIdConnectUrl?: string;\n [key: string]: unknown;\n}\n\n// ============================================================\n// API Info\n// ============================================================\n\nexport const apiInfo = {\n title: '${title}',\n version: '${version}',${description ? `\\n description: '${description.replace(/'/g, \"\\\\'\")}',` : ''}\n} as const;\n\n// ============================================================\n// Security Schemes\n// ============================================================\n\n/**\n * Available security schemes for this API.\n * Maps authorizer names to OpenAPI security scheme definitions.\n */\nexport const securitySchemes = ${JSON.stringify(securitySchemesObj, null, 2).replace(/\"([^\"]+)\":/g, '$1:')} as const satisfies Record<string, SecuritySchemeObject>;\n\nexport type SecuritySchemeId = ${schemeNames || 'never'};\n\n// ============================================================\n// Endpoint Authentication Map\n// ============================================================\n\n/**\n * Runtime map of endpoints to their required authentication scheme.\n * \\`null\\` indicates a public endpoint (no auth required).\n */\nexport const endpointAuth = ${JSON.stringify(endpointAuth, null, 2).replace(/\"([^\"]+)\":/g, \"'$1':\")} as const satisfies Record<string, SecuritySchemeId | null>;\n\nexport type EndpointString = keyof typeof endpointAuth;\n\nexport type AuthenticatedEndpoint = {\n [K in EndpointString]: typeof endpointAuth[K] extends null ? never : K;\n}[EndpointString];\n\nexport type PublicEndpoint = {\n [K in EndpointString]: typeof endpointAuth[K] extends null ? K : never;\n}[EndpointString];\n\n// ============================================================\n// Schema Definitions\n// ============================================================\n\n${schemaInterfaces}\n\n// ============================================================\n// OpenAPI Paths\n// ============================================================\n\n${pathsInterface}\n${createApiSection}\n`;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SubscriberInfo } from "./types-Cxl8-uwV.mjs";
|
|
2
|
+
import { BuildContext } from "./types-C4KITv-y.mjs";
|
|
3
|
+
import { ConstructGenerator, GeneratedConstruct, GeneratorOptions } from "./Generator-BjHK_qce.mjs";
|
|
4
|
+
import { Subscriber } from "@geekmidas/constructs/subscribers";
|
|
5
|
+
|
|
6
|
+
//#region src/generators/SubscriberGenerator.d.ts
|
|
7
|
+
declare class SubscriberGenerator extends ConstructGenerator<Subscriber<any, any, any, any, any, any>, SubscriberInfo[]> {
|
|
8
|
+
isConstruct(value: any): value is Subscriber<any, any, any, any, any, any>;
|
|
9
|
+
build(context: BuildContext, constructs: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[], outputDir: string, options?: GeneratorOptions): Promise<SubscriberInfo[]>;
|
|
10
|
+
private generateSubscriberHandler;
|
|
11
|
+
private generateServerSubscribersFile;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { SubscriberGenerator };
|
|
15
|
+
//# sourceMappingURL=SubscriberGenerator-7uX42xyG.d.mts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConstructGenerator } from "./Generator-
|
|
1
|
+
import { ConstructGenerator } from "./Generator-CDt4pB3W.mjs";
|
|
2
2
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join, relative } from "node:path";
|
|
4
4
|
import { Subscriber } from "@geekmidas/constructs/subscribers";
|
|
@@ -197,4 +197,4 @@ export async function setupSubscribers(
|
|
|
197
197
|
|
|
198
198
|
//#endregion
|
|
199
199
|
export { SubscriberGenerator };
|
|
200
|
-
//# sourceMappingURL=SubscriberGenerator-
|
|
200
|
+
//# sourceMappingURL=SubscriberGenerator-DABaJXML.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubscriberGenerator-Dnlj_1FK.mjs","names":["value: any","context: BuildContext","constructs: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[]","outputDir: string","options?: GeneratorOptions","subscriberInfos: SubscriberInfo[]","sourceFile: string","exportName: string","_subscriber: Subscriber<any, any, any, any, any, any>","subscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[]"],"sources":["../src/generators/SubscriberGenerator.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { Subscriber } from '@geekmidas/constructs/subscribers';\nimport type { BuildContext } from '../build/types';\nimport type { SubscriberInfo } from '../types';\nimport {\n ConstructGenerator,\n type GeneratedConstruct,\n type GeneratorOptions,\n} from './Generator';\n\nexport class SubscriberGenerator extends ConstructGenerator<\n Subscriber<any, any, any, any, any, any>,\n SubscriberInfo[]\n> {\n isConstruct(value: any): value is Subscriber<any, any, any, any, any, any> {\n return Subscriber.isSubscriber(value);\n }\n\n async build(\n context: BuildContext,\n constructs: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[],\n outputDir: string,\n options?: GeneratorOptions,\n ): Promise<SubscriberInfo[]> {\n const provider = options?.provider || 'aws-lambda';\n const logger = console;\n const subscriberInfos: SubscriberInfo[] = [];\n\n if (provider === 'server') {\n // Generate subscribers.ts for server-based polling (even if empty)\n await this.generateServerSubscribersFile(outputDir, constructs);\n\n logger.log(\n `Generated server subscribers file with ${constructs.length} subscribers (polling mode)`,\n );\n\n // Return empty array as server subscribers don't have individual handlers\n return subscriberInfos;\n }\n\n if (constructs.length === 0) {\n return subscriberInfos;\n }\n\n if (provider !== 'aws-lambda') {\n return subscriberInfos;\n }\n\n // Create subscribers subdirectory\n const subscribersDir = join(outputDir, 'subscribers');\n await mkdir(subscribersDir, { recursive: true });\n\n // Generate subscriber handlers\n for (const { key, construct, path } of constructs) {\n const handlerFile = await this.generateSubscriberHandler(\n subscribersDir,\n path.relative,\n key,\n construct,\n context,\n );\n\n subscriberInfos.push({\n name: key,\n handler: relative(process.cwd(), handlerFile).replace(\n /\\.ts$/,\n '.handler',\n ),\n subscribedEvents: construct.subscribedEvents || [],\n timeout: construct.timeout,\n memorySize: construct.memorySize,\n environment: await construct.getEnvironment(),\n });\n\n logger.log(`Generated subscriber handler: ${key}`);\n }\n\n return subscriberInfos;\n }\n\n private async generateSubscriberHandler(\n outputDir: string,\n sourceFile: string,\n exportName: string,\n _subscriber: Subscriber<any, any, any, any, any, any>,\n context: BuildContext,\n ): Promise<string> {\n const handlerFileName = `${exportName}.ts`;\n const handlerPath = join(outputDir, handlerFileName);\n\n const relativePath = relative(dirname(handlerPath), sourceFile);\n const importPath = relativePath.replace(/\\.ts$/, '.js');\n\n const relativeEnvParserPath = relative(\n dirname(handlerPath),\n context.envParserPath,\n );\n\n const content = `import { AWSLambdaSubscriber } from '@geekmidas/constructs/aws';\nimport { ${exportName} } from '${importPath}';\nimport ${context.envParserImportPattern} from '${relativeEnvParserPath}';\n\nconst adapter = new AWSLambdaSubscriber(envParser, ${exportName});\n\nexport const handler = adapter.handler;\n`;\n\n await writeFile(handlerPath, content);\n return handlerPath;\n }\n\n private async generateServerSubscribersFile(\n outputDir: string,\n subscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[],\n ): Promise<string> {\n // Ensure output directory exists\n await mkdir(outputDir, { recursive: true });\n\n const subscribersFileName = 'subscribers.ts';\n const subscribersPath = join(outputDir, subscribersFileName);\n\n // Group imports by file\n const importsByFile = new Map<string, string[]>();\n\n for (const { path, key } of subscribers) {\n const relativePath = relative(dirname(subscribersPath), path.relative);\n const importPath = relativePath.replace(/\\.ts$/, '.js');\n\n if (!importsByFile.has(importPath)) {\n importsByFile.set(importPath, []);\n }\n importsByFile.get(importPath)!.push(key);\n }\n\n // Generate import statements\n const imports = Array.from(importsByFile.entries())\n .map(\n ([importPath, exports]) =>\n `import { ${exports.join(', ')} } from '${importPath}';`,\n )\n .join('\\n');\n\n const allExportNames = subscribers.map(({ key }) => key);\n\n const content = `/**\n * Generated subscribers setup\n *\n * ⚠️ WARNING: This is for LOCAL DEVELOPMENT ONLY\n * This uses event polling which is not suitable for production.\n *\n * For production, use AWS Lambda with SQS/SNS event source mappings.\n * Lambda automatically:\n * - Scales based on queue depth\n * - Handles batch processing and retries\n * - Manages dead letter queues\n * - Provides better cost optimization\n *\n * This polling implementation is useful for:\n * - Local development and testing\n * - Understanding event flow without Lambda deployment\n *\n * Supported connection strings:\n * - sqs://region/account-id/queue-name (SQS queue)\n * - sns://region/account-id/topic-name (SNS topic)\n * - rabbitmq://host:port/queue-name (RabbitMQ)\n * - basic://in-memory (In-memory for testing)\n */\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport { EventConnectionFactory, Subscriber } from '@geekmidas/events';\nimport type { EventConnection, EventSubscriber } from '@geekmidas/events';\nimport { ServiceDiscovery } from '@geekmidas/services';\n${imports}\n\nconst subscribers = [\n ${allExportNames.join(',\\n ')}\n];\n\nconst activeSubscribers: EventSubscriber<any>[] = [];\n\nexport async function setupSubscribers(\n envParser: EnvironmentParser<any>,\n logger: Logger,\n): Promise<void> {\n logger.info('Setting up subscribers in polling mode (local development)');\n\n const config = envParser.create((get) => ({\n connectionString: get('EVENT_SUBSCRIBER_CONNECTION_STRING').string().optional(),\n })).parse();\n\n if (!config.connectionString) {\n logger.warn('EVENT_SUBSCRIBER_CONNECTION_STRING not configured, skipping subscriber setup');\n return;\n }\n\n const serviceDiscovery = ServiceDiscovery.getInstance(logger, envParser);\n\n // Create connection once, outside the loop (more efficient)\n // EventConnectionFactory automatically determines the right connection type\n let connection: EventConnection;\n try {\n connection = await EventConnectionFactory.fromConnectionString(config.connectionString);\n\n const connectionType = new URL(config.connectionString).protocol.replace(':', '');\n logger.info({ connectionType }, 'Created shared event connection');\n } catch (error) {\n logger.error({ error }, 'Failed to create event connection');\n return;\n }\n\n for (const subscriber of subscribers) {\n try {\n // Create subscriber from shared connection\n const eventSubscriber = await Subscriber.fromConnection(connection);\n\n // Register services\n const services = subscriber.services.length > 0\n ? await serviceDiscovery.register(subscriber.services)\n : {};\n\n // Subscribe to events\n const subscribedEvents = subscriber.subscribedEvents || [];\n\n if (subscribedEvents.length === 0) {\n logger.warn({ subscriber: subscriber.constructor.name }, 'Subscriber has no subscribed events, skipping');\n continue;\n }\n\n await eventSubscriber.subscribe(subscribedEvents, async (event) => {\n try {\n // Process single event (batch of 1)\n await subscriber.handler({\n events: [event],\n services: services as any,\n logger: subscriber.logger,\n });\n\n logger.debug({ eventType: event.type }, 'Successfully processed event');\n } catch (error) {\n logger.error({ error, event }, 'Failed to process event');\n // Event will become visible again for retry\n }\n });\n\n activeSubscribers.push(eventSubscriber);\n\n logger.info(\n {\n events: subscribedEvents,\n },\n 'Subscriber started polling'\n );\n } catch (error) {\n logger.error({ error, subscriber: subscriber.constructor.name }, 'Failed to setup subscriber');\n }\n }\n\n // Setup graceful shutdown\n const shutdown = () => {\n logger.info('Stopping all subscribers');\n for (const eventSubscriber of activeSubscribers) {\n connection.stop();\n }\n };\n\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n}\n`;\n\n await writeFile(subscribersPath, content);\n return subscribersPath;\n }\n}\n"],"mappings":";;;;;;AAWA,IAAa,sBAAb,cAAyC,mBAGvC;CACA,YAAYA,OAA+D;AACzE,SAAO,WAAW,aAAa,MAAM;CACtC;CAED,MAAM,MACJC,SACAC,YACAC,WACAC,SAC2B;EAC3B,MAAM,WAAW,SAAS,YAAY;EACtC,MAAM,SAAS;EACf,MAAMC,kBAAoC,CAAE;AAE5C,MAAI,aAAa,UAAU;AAEzB,SAAM,KAAK,8BAA8B,WAAW,WAAW;AAE/D,UAAO,KACJ,yCAAyC,WAAW,OAAO,6BAC7D;AAGD,UAAO;EACR;AAED,MAAI,WAAW,WAAW,EACxB,QAAO;AAGT,MAAI,aAAa,aACf,QAAO;EAIT,MAAM,iBAAiB,KAAK,WAAW,cAAc;AACrD,QAAM,MAAM,gBAAgB,EAAE,WAAW,KAAM,EAAC;AAGhD,OAAK,MAAM,EAAE,KAAK,WAAW,MAAM,IAAI,YAAY;GACjD,MAAM,cAAc,MAAM,KAAK,0BAC7B,gBACA,KAAK,UACL,KACA,WACA,QACD;AAED,mBAAgB,KAAK;IACnB,MAAM;IACN,SAAS,SAAS,QAAQ,KAAK,EAAE,YAAY,CAAC,QAC5C,SACA,WACD;IACD,kBAAkB,UAAU,oBAAoB,CAAE;IAClD,SAAS,UAAU;IACnB,YAAY,UAAU;IACtB,aAAa,MAAM,UAAU,gBAAgB;GAC9C,EAAC;AAEF,UAAO,KAAK,gCAAgC,IAAI,EAAE;EACnD;AAED,SAAO;CACR;CAED,MAAc,0BACZF,WACAG,YACAC,YACAC,aACAP,SACiB;EACjB,MAAM,mBAAmB,EAAE,WAAW;EACtC,MAAM,cAAc,KAAK,WAAW,gBAAgB;EAEpD,MAAM,eAAe,SAAS,QAAQ,YAAY,EAAE,WAAW;EAC/D,MAAM,aAAa,aAAa,QAAQ,SAAS,MAAM;EAEvD,MAAM,wBAAwB,SAC5B,QAAQ,YAAY,EACpB,QAAQ,cACT;EAED,MAAM,WAAW;WACV,WAAW,WAAW,WAAW;SACnC,QAAQ,uBAAuB,SAAS,sBAAsB;;qDAElB,WAAW;;;;AAK5D,QAAM,UAAU,aAAa,QAAQ;AACrC,SAAO;CACR;CAED,MAAc,8BACZE,WACAM,aACiB;AAEjB,QAAM,MAAM,WAAW,EAAE,WAAW,KAAM,EAAC;EAE3C,MAAM,sBAAsB;EAC5B,MAAM,kBAAkB,KAAK,WAAW,oBAAoB;EAG5D,MAAM,gCAAgB,IAAI;AAE1B,OAAK,MAAM,EAAE,MAAM,KAAK,IAAI,aAAa;GACvC,MAAM,eAAe,SAAS,QAAQ,gBAAgB,EAAE,KAAK,SAAS;GACtE,MAAM,aAAa,aAAa,QAAQ,SAAS,MAAM;AAEvD,QAAK,cAAc,IAAI,WAAW,CAChC,eAAc,IAAI,YAAY,CAAE,EAAC;AAEnC,iBAAc,IAAI,WAAW,CAAE,KAAK,IAAI;EACzC;EAGD,MAAM,UAAU,MAAM,KAAK,cAAc,SAAS,CAAC,CAChD,IACC,CAAC,CAAC,YAAY,QAAQ,MACnB,WAAW,QAAQ,KAAK,KAAK,CAAC,WAAW,WAAW,IACxD,CACA,KAAK,KAAK;EAEb,MAAM,iBAAiB,YAAY,IAAI,CAAC,EAAE,KAAK,KAAK,IAAI;EAExD,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BnB,QAAQ;;;IAGN,eAAe,KAAK,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+F7B,QAAM,UAAU,iBAAiB,QAAQ;AACzC,SAAO;CACR;AACF"}
|
|
1
|
+
{"version":3,"file":"SubscriberGenerator-DABaJXML.mjs","names":["value: any","context: BuildContext","constructs: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[]","outputDir: string","options?: GeneratorOptions","subscriberInfos: SubscriberInfo[]","sourceFile: string","exportName: string","_subscriber: Subscriber<any, any, any, any, any, any>","subscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[]"],"sources":["../src/generators/SubscriberGenerator.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { Subscriber } from '@geekmidas/constructs/subscribers';\nimport type { BuildContext } from '../build/types';\nimport type { SubscriberInfo } from '../types';\nimport {\n ConstructGenerator,\n type GeneratedConstruct,\n type GeneratorOptions,\n} from './Generator';\n\nexport class SubscriberGenerator extends ConstructGenerator<\n Subscriber<any, any, any, any, any, any>,\n SubscriberInfo[]\n> {\n isConstruct(value: any): value is Subscriber<any, any, any, any, any, any> {\n return Subscriber.isSubscriber(value);\n }\n\n async build(\n context: BuildContext,\n constructs: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[],\n outputDir: string,\n options?: GeneratorOptions,\n ): Promise<SubscriberInfo[]> {\n const provider = options?.provider || 'aws-lambda';\n const logger = console;\n const subscriberInfos: SubscriberInfo[] = [];\n\n if (provider === 'server') {\n // Generate subscribers.ts for server-based polling (even if empty)\n await this.generateServerSubscribersFile(outputDir, constructs);\n\n logger.log(\n `Generated server subscribers file with ${constructs.length} subscribers (polling mode)`,\n );\n\n // Return empty array as server subscribers don't have individual handlers\n return subscriberInfos;\n }\n\n if (constructs.length === 0) {\n return subscriberInfos;\n }\n\n if (provider !== 'aws-lambda') {\n return subscriberInfos;\n }\n\n // Create subscribers subdirectory\n const subscribersDir = join(outputDir, 'subscribers');\n await mkdir(subscribersDir, { recursive: true });\n\n // Generate subscriber handlers\n for (const { key, construct, path } of constructs) {\n const handlerFile = await this.generateSubscriberHandler(\n subscribersDir,\n path.relative,\n key,\n construct,\n context,\n );\n\n subscriberInfos.push({\n name: key,\n handler: relative(process.cwd(), handlerFile).replace(\n /\\.ts$/,\n '.handler',\n ),\n subscribedEvents: construct.subscribedEvents || [],\n timeout: construct.timeout,\n memorySize: construct.memorySize,\n environment: await construct.getEnvironment(),\n });\n\n logger.log(`Generated subscriber handler: ${key}`);\n }\n\n return subscriberInfos;\n }\n\n private async generateSubscriberHandler(\n outputDir: string,\n sourceFile: string,\n exportName: string,\n _subscriber: Subscriber<any, any, any, any, any, any>,\n context: BuildContext,\n ): Promise<string> {\n const handlerFileName = `${exportName}.ts`;\n const handlerPath = join(outputDir, handlerFileName);\n\n const relativePath = relative(dirname(handlerPath), sourceFile);\n const importPath = relativePath.replace(/\\.ts$/, '.js');\n\n const relativeEnvParserPath = relative(\n dirname(handlerPath),\n context.envParserPath,\n );\n\n const content = `import { AWSLambdaSubscriber } from '@geekmidas/constructs/aws';\nimport { ${exportName} } from '${importPath}';\nimport ${context.envParserImportPattern} from '${relativeEnvParserPath}';\n\nconst adapter = new AWSLambdaSubscriber(envParser, ${exportName});\n\nexport const handler = adapter.handler;\n`;\n\n await writeFile(handlerPath, content);\n return handlerPath;\n }\n\n private async generateServerSubscribersFile(\n outputDir: string,\n subscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[],\n ): Promise<string> {\n // Ensure output directory exists\n await mkdir(outputDir, { recursive: true });\n\n const subscribersFileName = 'subscribers.ts';\n const subscribersPath = join(outputDir, subscribersFileName);\n\n // Group imports by file\n const importsByFile = new Map<string, string[]>();\n\n for (const { path, key } of subscribers) {\n const relativePath = relative(dirname(subscribersPath), path.relative);\n const importPath = relativePath.replace(/\\.ts$/, '.js');\n\n if (!importsByFile.has(importPath)) {\n importsByFile.set(importPath, []);\n }\n importsByFile.get(importPath)!.push(key);\n }\n\n // Generate import statements\n const imports = Array.from(importsByFile.entries())\n .map(\n ([importPath, exports]) =>\n `import { ${exports.join(', ')} } from '${importPath}';`,\n )\n .join('\\n');\n\n const allExportNames = subscribers.map(({ key }) => key);\n\n const content = `/**\n * Generated subscribers setup\n *\n * ⚠️ WARNING: This is for LOCAL DEVELOPMENT ONLY\n * This uses event polling which is not suitable for production.\n *\n * For production, use AWS Lambda with SQS/SNS event source mappings.\n * Lambda automatically:\n * - Scales based on queue depth\n * - Handles batch processing and retries\n * - Manages dead letter queues\n * - Provides better cost optimization\n *\n * This polling implementation is useful for:\n * - Local development and testing\n * - Understanding event flow without Lambda deployment\n *\n * Supported connection strings:\n * - sqs://region/account-id/queue-name (SQS queue)\n * - sns://region/account-id/topic-name (SNS topic)\n * - rabbitmq://host:port/queue-name (RabbitMQ)\n * - basic://in-memory (In-memory for testing)\n */\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport { EventConnectionFactory, Subscriber } from '@geekmidas/events';\nimport type { EventConnection, EventSubscriber } from '@geekmidas/events';\nimport { ServiceDiscovery } from '@geekmidas/services';\n${imports}\n\nconst subscribers = [\n ${allExportNames.join(',\\n ')}\n];\n\nconst activeSubscribers: EventSubscriber<any>[] = [];\n\nexport async function setupSubscribers(\n envParser: EnvironmentParser<any>,\n logger: Logger,\n): Promise<void> {\n logger.info('Setting up subscribers in polling mode (local development)');\n\n const config = envParser.create((get) => ({\n connectionString: get('EVENT_SUBSCRIBER_CONNECTION_STRING').string().optional(),\n })).parse();\n\n if (!config.connectionString) {\n logger.warn('EVENT_SUBSCRIBER_CONNECTION_STRING not configured, skipping subscriber setup');\n return;\n }\n\n const serviceDiscovery = ServiceDiscovery.getInstance(logger, envParser);\n\n // Create connection once, outside the loop (more efficient)\n // EventConnectionFactory automatically determines the right connection type\n let connection: EventConnection;\n try {\n connection = await EventConnectionFactory.fromConnectionString(config.connectionString);\n\n const connectionType = new URL(config.connectionString).protocol.replace(':', '');\n logger.info({ connectionType }, 'Created shared event connection');\n } catch (error) {\n logger.error({ error }, 'Failed to create event connection');\n return;\n }\n\n for (const subscriber of subscribers) {\n try {\n // Create subscriber from shared connection\n const eventSubscriber = await Subscriber.fromConnection(connection);\n\n // Register services\n const services = subscriber.services.length > 0\n ? await serviceDiscovery.register(subscriber.services)\n : {};\n\n // Subscribe to events\n const subscribedEvents = subscriber.subscribedEvents || [];\n\n if (subscribedEvents.length === 0) {\n logger.warn({ subscriber: subscriber.constructor.name }, 'Subscriber has no subscribed events, skipping');\n continue;\n }\n\n await eventSubscriber.subscribe(subscribedEvents, async (event) => {\n try {\n // Process single event (batch of 1)\n await subscriber.handler({\n events: [event],\n services: services as any,\n logger: subscriber.logger,\n });\n\n logger.debug({ eventType: event.type }, 'Successfully processed event');\n } catch (error) {\n logger.error({ error, event }, 'Failed to process event');\n // Event will become visible again for retry\n }\n });\n\n activeSubscribers.push(eventSubscriber);\n\n logger.info(\n {\n events: subscribedEvents,\n },\n 'Subscriber started polling'\n );\n } catch (error) {\n logger.error({ error, subscriber: subscriber.constructor.name }, 'Failed to setup subscriber');\n }\n }\n\n // Setup graceful shutdown\n const shutdown = () => {\n logger.info('Stopping all subscribers');\n for (const eventSubscriber of activeSubscribers) {\n connection.stop();\n }\n };\n\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n}\n`;\n\n await writeFile(subscribersPath, content);\n return subscribersPath;\n }\n}\n"],"mappings":";;;;;;AAWA,IAAa,sBAAb,cAAyC,mBAGvC;CACA,YAAYA,OAA+D;AACzE,SAAO,WAAW,aAAa,MAAM;CACtC;CAED,MAAM,MACJC,SACAC,YACAC,WACAC,SAC2B;EAC3B,MAAM,WAAW,SAAS,YAAY;EACtC,MAAM,SAAS;EACf,MAAMC,kBAAoC,CAAE;AAE5C,MAAI,aAAa,UAAU;AAEzB,SAAM,KAAK,8BAA8B,WAAW,WAAW;AAE/D,UAAO,KACJ,yCAAyC,WAAW,OAAO,6BAC7D;AAGD,UAAO;EACR;AAED,MAAI,WAAW,WAAW,EACxB,QAAO;AAGT,MAAI,aAAa,aACf,QAAO;EAIT,MAAM,iBAAiB,KAAK,WAAW,cAAc;AACrD,QAAM,MAAM,gBAAgB,EAAE,WAAW,KAAM,EAAC;AAGhD,OAAK,MAAM,EAAE,KAAK,WAAW,MAAM,IAAI,YAAY;GACjD,MAAM,cAAc,MAAM,KAAK,0BAC7B,gBACA,KAAK,UACL,KACA,WACA,QACD;AAED,mBAAgB,KAAK;IACnB,MAAM;IACN,SAAS,SAAS,QAAQ,KAAK,EAAE,YAAY,CAAC,QAC5C,SACA,WACD;IACD,kBAAkB,UAAU,oBAAoB,CAAE;IAClD,SAAS,UAAU;IACnB,YAAY,UAAU;IACtB,aAAa,MAAM,UAAU,gBAAgB;GAC9C,EAAC;AAEF,UAAO,KAAK,gCAAgC,IAAI,EAAE;EACnD;AAED,SAAO;CACR;CAED,MAAc,0BACZF,WACAG,YACAC,YACAC,aACAP,SACiB;EACjB,MAAM,mBAAmB,EAAE,WAAW;EACtC,MAAM,cAAc,KAAK,WAAW,gBAAgB;EAEpD,MAAM,eAAe,SAAS,QAAQ,YAAY,EAAE,WAAW;EAC/D,MAAM,aAAa,aAAa,QAAQ,SAAS,MAAM;EAEvD,MAAM,wBAAwB,SAC5B,QAAQ,YAAY,EACpB,QAAQ,cACT;EAED,MAAM,WAAW;WACV,WAAW,WAAW,WAAW;SACnC,QAAQ,uBAAuB,SAAS,sBAAsB;;qDAElB,WAAW;;;;AAK5D,QAAM,UAAU,aAAa,QAAQ;AACrC,SAAO;CACR;CAED,MAAc,8BACZE,WACAM,aACiB;AAEjB,QAAM,MAAM,WAAW,EAAE,WAAW,KAAM,EAAC;EAE3C,MAAM,sBAAsB;EAC5B,MAAM,kBAAkB,KAAK,WAAW,oBAAoB;EAG5D,MAAM,gCAAgB,IAAI;AAE1B,OAAK,MAAM,EAAE,MAAM,KAAK,IAAI,aAAa;GACvC,MAAM,eAAe,SAAS,QAAQ,gBAAgB,EAAE,KAAK,SAAS;GACtE,MAAM,aAAa,aAAa,QAAQ,SAAS,MAAM;AAEvD,QAAK,cAAc,IAAI,WAAW,CAChC,eAAc,IAAI,YAAY,CAAE,EAAC;AAEnC,iBAAc,IAAI,WAAW,CAAE,KAAK,IAAI;EACzC;EAGD,MAAM,UAAU,MAAM,KAAK,cAAc,SAAS,CAAC,CAChD,IACC,CAAC,CAAC,YAAY,QAAQ,MACnB,WAAW,QAAQ,KAAK,KAAK,CAAC,WAAW,WAAW,IACxD,CACA,KAAK,KAAK;EAEb,MAAM,iBAAiB,YAAY,IAAI,CAAC,EAAE,KAAK,KAAK,IAAI;EAExD,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BnB,QAAQ;;;IAGN,eAAe,KAAK,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+F7B,QAAM,UAAU,iBAAiB,QAAQ;AACzC,SAAO;CACR;AACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require('./chunk-CUT6urMc.cjs');
|
|
2
|
-
const require_Generator = require('./Generator-
|
|
2
|
+
const require_Generator = require('./Generator-CLVplqm2.cjs');
|
|
3
3
|
const node_fs_promises = require_chunk.__toESM(require("node:fs/promises"));
|
|
4
4
|
const node_path = require_chunk.__toESM(require("node:path"));
|
|
5
5
|
const __geekmidas_constructs_subscribers = require_chunk.__toESM(require("@geekmidas/constructs/subscribers"));
|
|
@@ -203,4 +203,4 @@ Object.defineProperty(exports, 'SubscriberGenerator', {
|
|
|
203
203
|
return SubscriberGenerator;
|
|
204
204
|
}
|
|
205
205
|
});
|
|
206
|
-
//# sourceMappingURL=SubscriberGenerator-
|
|
206
|
+
//# sourceMappingURL=SubscriberGenerator-D_zpNGFr.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubscriberGenerator-Bd-a7aiw.cjs","names":["ConstructGenerator","value: any","context: BuildContext","constructs: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[]","outputDir: string","options?: GeneratorOptions","subscriberInfos: SubscriberInfo[]","sourceFile: string","exportName: string","_subscriber: Subscriber<any, any, any, any, any, any>","subscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[]","exports"],"sources":["../src/generators/SubscriberGenerator.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { Subscriber } from '@geekmidas/constructs/subscribers';\nimport type { BuildContext } from '../build/types';\nimport type { SubscriberInfo } from '../types';\nimport {\n ConstructGenerator,\n type GeneratedConstruct,\n type GeneratorOptions,\n} from './Generator';\n\nexport class SubscriberGenerator extends ConstructGenerator<\n Subscriber<any, any, any, any, any, any>,\n SubscriberInfo[]\n> {\n isConstruct(value: any): value is Subscriber<any, any, any, any, any, any> {\n return Subscriber.isSubscriber(value);\n }\n\n async build(\n context: BuildContext,\n constructs: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[],\n outputDir: string,\n options?: GeneratorOptions,\n ): Promise<SubscriberInfo[]> {\n const provider = options?.provider || 'aws-lambda';\n const logger = console;\n const subscriberInfos: SubscriberInfo[] = [];\n\n if (provider === 'server') {\n // Generate subscribers.ts for server-based polling (even if empty)\n await this.generateServerSubscribersFile(outputDir, constructs);\n\n logger.log(\n `Generated server subscribers file with ${constructs.length} subscribers (polling mode)`,\n );\n\n // Return empty array as server subscribers don't have individual handlers\n return subscriberInfos;\n }\n\n if (constructs.length === 0) {\n return subscriberInfos;\n }\n\n if (provider !== 'aws-lambda') {\n return subscriberInfos;\n }\n\n // Create subscribers subdirectory\n const subscribersDir = join(outputDir, 'subscribers');\n await mkdir(subscribersDir, { recursive: true });\n\n // Generate subscriber handlers\n for (const { key, construct, path } of constructs) {\n const handlerFile = await this.generateSubscriberHandler(\n subscribersDir,\n path.relative,\n key,\n construct,\n context,\n );\n\n subscriberInfos.push({\n name: key,\n handler: relative(process.cwd(), handlerFile).replace(\n /\\.ts$/,\n '.handler',\n ),\n subscribedEvents: construct.subscribedEvents || [],\n timeout: construct.timeout,\n memorySize: construct.memorySize,\n environment: await construct.getEnvironment(),\n });\n\n logger.log(`Generated subscriber handler: ${key}`);\n }\n\n return subscriberInfos;\n }\n\n private async generateSubscriberHandler(\n outputDir: string,\n sourceFile: string,\n exportName: string,\n _subscriber: Subscriber<any, any, any, any, any, any>,\n context: BuildContext,\n ): Promise<string> {\n const handlerFileName = `${exportName}.ts`;\n const handlerPath = join(outputDir, handlerFileName);\n\n const relativePath = relative(dirname(handlerPath), sourceFile);\n const importPath = relativePath.replace(/\\.ts$/, '.js');\n\n const relativeEnvParserPath = relative(\n dirname(handlerPath),\n context.envParserPath,\n );\n\n const content = `import { AWSLambdaSubscriber } from '@geekmidas/constructs/aws';\nimport { ${exportName} } from '${importPath}';\nimport ${context.envParserImportPattern} from '${relativeEnvParserPath}';\n\nconst adapter = new AWSLambdaSubscriber(envParser, ${exportName});\n\nexport const handler = adapter.handler;\n`;\n\n await writeFile(handlerPath, content);\n return handlerPath;\n }\n\n private async generateServerSubscribersFile(\n outputDir: string,\n subscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[],\n ): Promise<string> {\n // Ensure output directory exists\n await mkdir(outputDir, { recursive: true });\n\n const subscribersFileName = 'subscribers.ts';\n const subscribersPath = join(outputDir, subscribersFileName);\n\n // Group imports by file\n const importsByFile = new Map<string, string[]>();\n\n for (const { path, key } of subscribers) {\n const relativePath = relative(dirname(subscribersPath), path.relative);\n const importPath = relativePath.replace(/\\.ts$/, '.js');\n\n if (!importsByFile.has(importPath)) {\n importsByFile.set(importPath, []);\n }\n importsByFile.get(importPath)!.push(key);\n }\n\n // Generate import statements\n const imports = Array.from(importsByFile.entries())\n .map(\n ([importPath, exports]) =>\n `import { ${exports.join(', ')} } from '${importPath}';`,\n )\n .join('\\n');\n\n const allExportNames = subscribers.map(({ key }) => key);\n\n const content = `/**\n * Generated subscribers setup\n *\n * ⚠️ WARNING: This is for LOCAL DEVELOPMENT ONLY\n * This uses event polling which is not suitable for production.\n *\n * For production, use AWS Lambda with SQS/SNS event source mappings.\n * Lambda automatically:\n * - Scales based on queue depth\n * - Handles batch processing and retries\n * - Manages dead letter queues\n * - Provides better cost optimization\n *\n * This polling implementation is useful for:\n * - Local development and testing\n * - Understanding event flow without Lambda deployment\n *\n * Supported connection strings:\n * - sqs://region/account-id/queue-name (SQS queue)\n * - sns://region/account-id/topic-name (SNS topic)\n * - rabbitmq://host:port/queue-name (RabbitMQ)\n * - basic://in-memory (In-memory for testing)\n */\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport { EventConnectionFactory, Subscriber } from '@geekmidas/events';\nimport type { EventConnection, EventSubscriber } from '@geekmidas/events';\nimport { ServiceDiscovery } from '@geekmidas/services';\n${imports}\n\nconst subscribers = [\n ${allExportNames.join(',\\n ')}\n];\n\nconst activeSubscribers: EventSubscriber<any>[] = [];\n\nexport async function setupSubscribers(\n envParser: EnvironmentParser<any>,\n logger: Logger,\n): Promise<void> {\n logger.info('Setting up subscribers in polling mode (local development)');\n\n const config = envParser.create((get) => ({\n connectionString: get('EVENT_SUBSCRIBER_CONNECTION_STRING').string().optional(),\n })).parse();\n\n if (!config.connectionString) {\n logger.warn('EVENT_SUBSCRIBER_CONNECTION_STRING not configured, skipping subscriber setup');\n return;\n }\n\n const serviceDiscovery = ServiceDiscovery.getInstance(logger, envParser);\n\n // Create connection once, outside the loop (more efficient)\n // EventConnectionFactory automatically determines the right connection type\n let connection: EventConnection;\n try {\n connection = await EventConnectionFactory.fromConnectionString(config.connectionString);\n\n const connectionType = new URL(config.connectionString).protocol.replace(':', '');\n logger.info({ connectionType }, 'Created shared event connection');\n } catch (error) {\n logger.error({ error }, 'Failed to create event connection');\n return;\n }\n\n for (const subscriber of subscribers) {\n try {\n // Create subscriber from shared connection\n const eventSubscriber = await Subscriber.fromConnection(connection);\n\n // Register services\n const services = subscriber.services.length > 0\n ? await serviceDiscovery.register(subscriber.services)\n : {};\n\n // Subscribe to events\n const subscribedEvents = subscriber.subscribedEvents || [];\n\n if (subscribedEvents.length === 0) {\n logger.warn({ subscriber: subscriber.constructor.name }, 'Subscriber has no subscribed events, skipping');\n continue;\n }\n\n await eventSubscriber.subscribe(subscribedEvents, async (event) => {\n try {\n // Process single event (batch of 1)\n await subscriber.handler({\n events: [event],\n services: services as any,\n logger: subscriber.logger,\n });\n\n logger.debug({ eventType: event.type }, 'Successfully processed event');\n } catch (error) {\n logger.error({ error, event }, 'Failed to process event');\n // Event will become visible again for retry\n }\n });\n\n activeSubscribers.push(eventSubscriber);\n\n logger.info(\n {\n events: subscribedEvents,\n },\n 'Subscriber started polling'\n );\n } catch (error) {\n logger.error({ error, subscriber: subscriber.constructor.name }, 'Failed to setup subscriber');\n }\n }\n\n // Setup graceful shutdown\n const shutdown = () => {\n logger.info('Stopping all subscribers');\n for (const eventSubscriber of activeSubscribers) {\n connection.stop();\n }\n };\n\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n}\n`;\n\n await writeFile(subscribersPath, content);\n return subscribersPath;\n }\n}\n"],"mappings":";;;;;;;AAWA,IAAa,sBAAb,cAAyCA,qCAGvC;CACA,YAAYC,OAA+D;AACzE,SAAO,8CAAW,aAAa,MAAM;CACtC;CAED,MAAM,MACJC,SACAC,YACAC,WACAC,SAC2B;EAC3B,MAAM,WAAW,SAAS,YAAY;EACtC,MAAM,SAAS;EACf,MAAMC,kBAAoC,CAAE;AAE5C,MAAI,aAAa,UAAU;AAEzB,SAAM,KAAK,8BAA8B,WAAW,WAAW;AAE/D,UAAO,KACJ,yCAAyC,WAAW,OAAO,6BAC7D;AAGD,UAAO;EACR;AAED,MAAI,WAAW,WAAW,EACxB,QAAO;AAGT,MAAI,aAAa,aACf,QAAO;EAIT,MAAM,iBAAiB,oBAAK,WAAW,cAAc;AACrD,QAAM,4BAAM,gBAAgB,EAAE,WAAW,KAAM,EAAC;AAGhD,OAAK,MAAM,EAAE,KAAK,WAAW,MAAM,IAAI,YAAY;GACjD,MAAM,cAAc,MAAM,KAAK,0BAC7B,gBACA,KAAK,UACL,KACA,WACA,QACD;AAED,mBAAgB,KAAK;IACnB,MAAM;IACN,SAAS,wBAAS,QAAQ,KAAK,EAAE,YAAY,CAAC,QAC5C,SACA,WACD;IACD,kBAAkB,UAAU,oBAAoB,CAAE;IAClD,SAAS,UAAU;IACnB,YAAY,UAAU;IACtB,aAAa,MAAM,UAAU,gBAAgB;GAC9C,EAAC;AAEF,UAAO,KAAK,gCAAgC,IAAI,EAAE;EACnD;AAED,SAAO;CACR;CAED,MAAc,0BACZF,WACAG,YACAC,YACAC,aACAP,SACiB;EACjB,MAAM,mBAAmB,EAAE,WAAW;EACtC,MAAM,cAAc,oBAAK,WAAW,gBAAgB;EAEpD,MAAM,eAAe,wBAAS,uBAAQ,YAAY,EAAE,WAAW;EAC/D,MAAM,aAAa,aAAa,QAAQ,SAAS,MAAM;EAEvD,MAAM,wBAAwB,wBAC5B,uBAAQ,YAAY,EACpB,QAAQ,cACT;EAED,MAAM,WAAW;WACV,WAAW,WAAW,WAAW;SACnC,QAAQ,uBAAuB,SAAS,sBAAsB;;qDAElB,WAAW;;;;AAK5D,QAAM,gCAAU,aAAa,QAAQ;AACrC,SAAO;CACR;CAED,MAAc,8BACZE,WACAM,aACiB;AAEjB,QAAM,4BAAM,WAAW,EAAE,WAAW,KAAM,EAAC;EAE3C,MAAM,sBAAsB;EAC5B,MAAM,kBAAkB,oBAAK,WAAW,oBAAoB;EAG5D,MAAM,gCAAgB,IAAI;AAE1B,OAAK,MAAM,EAAE,MAAM,KAAK,IAAI,aAAa;GACvC,MAAM,eAAe,wBAAS,uBAAQ,gBAAgB,EAAE,KAAK,SAAS;GACtE,MAAM,aAAa,aAAa,QAAQ,SAAS,MAAM;AAEvD,QAAK,cAAc,IAAI,WAAW,CAChC,eAAc,IAAI,YAAY,CAAE,EAAC;AAEnC,iBAAc,IAAI,WAAW,CAAE,KAAK,IAAI;EACzC;EAGD,MAAM,UAAU,MAAM,KAAK,cAAc,SAAS,CAAC,CAChD,IACC,CAAC,CAAC,YAAYC,UAAQ,MACnB,WAAW,UAAQ,KAAK,KAAK,CAAC,WAAW,WAAW,IACxD,CACA,KAAK,KAAK;EAEb,MAAM,iBAAiB,YAAY,IAAI,CAAC,EAAE,KAAK,KAAK,IAAI;EAExD,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BnB,QAAQ;;;IAGN,eAAe,KAAK,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+F7B,QAAM,gCAAU,iBAAiB,QAAQ;AACzC,SAAO;CACR;AACF"}
|
|
1
|
+
{"version":3,"file":"SubscriberGenerator-D_zpNGFr.cjs","names":["ConstructGenerator","value: any","context: BuildContext","constructs: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[]","outputDir: string","options?: GeneratorOptions","subscriberInfos: SubscriberInfo[]","sourceFile: string","exportName: string","_subscriber: Subscriber<any, any, any, any, any, any>","subscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[]","exports"],"sources":["../src/generators/SubscriberGenerator.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { Subscriber } from '@geekmidas/constructs/subscribers';\nimport type { BuildContext } from '../build/types';\nimport type { SubscriberInfo } from '../types';\nimport {\n ConstructGenerator,\n type GeneratedConstruct,\n type GeneratorOptions,\n} from './Generator';\n\nexport class SubscriberGenerator extends ConstructGenerator<\n Subscriber<any, any, any, any, any, any>,\n SubscriberInfo[]\n> {\n isConstruct(value: any): value is Subscriber<any, any, any, any, any, any> {\n return Subscriber.isSubscriber(value);\n }\n\n async build(\n context: BuildContext,\n constructs: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[],\n outputDir: string,\n options?: GeneratorOptions,\n ): Promise<SubscriberInfo[]> {\n const provider = options?.provider || 'aws-lambda';\n const logger = console;\n const subscriberInfos: SubscriberInfo[] = [];\n\n if (provider === 'server') {\n // Generate subscribers.ts for server-based polling (even if empty)\n await this.generateServerSubscribersFile(outputDir, constructs);\n\n logger.log(\n `Generated server subscribers file with ${constructs.length} subscribers (polling mode)`,\n );\n\n // Return empty array as server subscribers don't have individual handlers\n return subscriberInfos;\n }\n\n if (constructs.length === 0) {\n return subscriberInfos;\n }\n\n if (provider !== 'aws-lambda') {\n return subscriberInfos;\n }\n\n // Create subscribers subdirectory\n const subscribersDir = join(outputDir, 'subscribers');\n await mkdir(subscribersDir, { recursive: true });\n\n // Generate subscriber handlers\n for (const { key, construct, path } of constructs) {\n const handlerFile = await this.generateSubscriberHandler(\n subscribersDir,\n path.relative,\n key,\n construct,\n context,\n );\n\n subscriberInfos.push({\n name: key,\n handler: relative(process.cwd(), handlerFile).replace(\n /\\.ts$/,\n '.handler',\n ),\n subscribedEvents: construct.subscribedEvents || [],\n timeout: construct.timeout,\n memorySize: construct.memorySize,\n environment: await construct.getEnvironment(),\n });\n\n logger.log(`Generated subscriber handler: ${key}`);\n }\n\n return subscriberInfos;\n }\n\n private async generateSubscriberHandler(\n outputDir: string,\n sourceFile: string,\n exportName: string,\n _subscriber: Subscriber<any, any, any, any, any, any>,\n context: BuildContext,\n ): Promise<string> {\n const handlerFileName = `${exportName}.ts`;\n const handlerPath = join(outputDir, handlerFileName);\n\n const relativePath = relative(dirname(handlerPath), sourceFile);\n const importPath = relativePath.replace(/\\.ts$/, '.js');\n\n const relativeEnvParserPath = relative(\n dirname(handlerPath),\n context.envParserPath,\n );\n\n const content = `import { AWSLambdaSubscriber } from '@geekmidas/constructs/aws';\nimport { ${exportName} } from '${importPath}';\nimport ${context.envParserImportPattern} from '${relativeEnvParserPath}';\n\nconst adapter = new AWSLambdaSubscriber(envParser, ${exportName});\n\nexport const handler = adapter.handler;\n`;\n\n await writeFile(handlerPath, content);\n return handlerPath;\n }\n\n private async generateServerSubscribersFile(\n outputDir: string,\n subscribers: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[],\n ): Promise<string> {\n // Ensure output directory exists\n await mkdir(outputDir, { recursive: true });\n\n const subscribersFileName = 'subscribers.ts';\n const subscribersPath = join(outputDir, subscribersFileName);\n\n // Group imports by file\n const importsByFile = new Map<string, string[]>();\n\n for (const { path, key } of subscribers) {\n const relativePath = relative(dirname(subscribersPath), path.relative);\n const importPath = relativePath.replace(/\\.ts$/, '.js');\n\n if (!importsByFile.has(importPath)) {\n importsByFile.set(importPath, []);\n }\n importsByFile.get(importPath)!.push(key);\n }\n\n // Generate import statements\n const imports = Array.from(importsByFile.entries())\n .map(\n ([importPath, exports]) =>\n `import { ${exports.join(', ')} } from '${importPath}';`,\n )\n .join('\\n');\n\n const allExportNames = subscribers.map(({ key }) => key);\n\n const content = `/**\n * Generated subscribers setup\n *\n * ⚠️ WARNING: This is for LOCAL DEVELOPMENT ONLY\n * This uses event polling which is not suitable for production.\n *\n * For production, use AWS Lambda with SQS/SNS event source mappings.\n * Lambda automatically:\n * - Scales based on queue depth\n * - Handles batch processing and retries\n * - Manages dead letter queues\n * - Provides better cost optimization\n *\n * This polling implementation is useful for:\n * - Local development and testing\n * - Understanding event flow without Lambda deployment\n *\n * Supported connection strings:\n * - sqs://region/account-id/queue-name (SQS queue)\n * - sns://region/account-id/topic-name (SNS topic)\n * - rabbitmq://host:port/queue-name (RabbitMQ)\n * - basic://in-memory (In-memory for testing)\n */\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport { EventConnectionFactory, Subscriber } from '@geekmidas/events';\nimport type { EventConnection, EventSubscriber } from '@geekmidas/events';\nimport { ServiceDiscovery } from '@geekmidas/services';\n${imports}\n\nconst subscribers = [\n ${allExportNames.join(',\\n ')}\n];\n\nconst activeSubscribers: EventSubscriber<any>[] = [];\n\nexport async function setupSubscribers(\n envParser: EnvironmentParser<any>,\n logger: Logger,\n): Promise<void> {\n logger.info('Setting up subscribers in polling mode (local development)');\n\n const config = envParser.create((get) => ({\n connectionString: get('EVENT_SUBSCRIBER_CONNECTION_STRING').string().optional(),\n })).parse();\n\n if (!config.connectionString) {\n logger.warn('EVENT_SUBSCRIBER_CONNECTION_STRING not configured, skipping subscriber setup');\n return;\n }\n\n const serviceDiscovery = ServiceDiscovery.getInstance(logger, envParser);\n\n // Create connection once, outside the loop (more efficient)\n // EventConnectionFactory automatically determines the right connection type\n let connection: EventConnection;\n try {\n connection = await EventConnectionFactory.fromConnectionString(config.connectionString);\n\n const connectionType = new URL(config.connectionString).protocol.replace(':', '');\n logger.info({ connectionType }, 'Created shared event connection');\n } catch (error) {\n logger.error({ error }, 'Failed to create event connection');\n return;\n }\n\n for (const subscriber of subscribers) {\n try {\n // Create subscriber from shared connection\n const eventSubscriber = await Subscriber.fromConnection(connection);\n\n // Register services\n const services = subscriber.services.length > 0\n ? await serviceDiscovery.register(subscriber.services)\n : {};\n\n // Subscribe to events\n const subscribedEvents = subscriber.subscribedEvents || [];\n\n if (subscribedEvents.length === 0) {\n logger.warn({ subscriber: subscriber.constructor.name }, 'Subscriber has no subscribed events, skipping');\n continue;\n }\n\n await eventSubscriber.subscribe(subscribedEvents, async (event) => {\n try {\n // Process single event (batch of 1)\n await subscriber.handler({\n events: [event],\n services: services as any,\n logger: subscriber.logger,\n });\n\n logger.debug({ eventType: event.type }, 'Successfully processed event');\n } catch (error) {\n logger.error({ error, event }, 'Failed to process event');\n // Event will become visible again for retry\n }\n });\n\n activeSubscribers.push(eventSubscriber);\n\n logger.info(\n {\n events: subscribedEvents,\n },\n 'Subscriber started polling'\n );\n } catch (error) {\n logger.error({ error, subscriber: subscriber.constructor.name }, 'Failed to setup subscriber');\n }\n }\n\n // Setup graceful shutdown\n const shutdown = () => {\n logger.info('Stopping all subscribers');\n for (const eventSubscriber of activeSubscribers) {\n connection.stop();\n }\n };\n\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n}\n`;\n\n await writeFile(subscribersPath, content);\n return subscribersPath;\n }\n}\n"],"mappings":";;;;;;;AAWA,IAAa,sBAAb,cAAyCA,qCAGvC;CACA,YAAYC,OAA+D;AACzE,SAAO,8CAAW,aAAa,MAAM;CACtC;CAED,MAAM,MACJC,SACAC,YACAC,WACAC,SAC2B;EAC3B,MAAM,WAAW,SAAS,YAAY;EACtC,MAAM,SAAS;EACf,MAAMC,kBAAoC,CAAE;AAE5C,MAAI,aAAa,UAAU;AAEzB,SAAM,KAAK,8BAA8B,WAAW,WAAW;AAE/D,UAAO,KACJ,yCAAyC,WAAW,OAAO,6BAC7D;AAGD,UAAO;EACR;AAED,MAAI,WAAW,WAAW,EACxB,QAAO;AAGT,MAAI,aAAa,aACf,QAAO;EAIT,MAAM,iBAAiB,oBAAK,WAAW,cAAc;AACrD,QAAM,4BAAM,gBAAgB,EAAE,WAAW,KAAM,EAAC;AAGhD,OAAK,MAAM,EAAE,KAAK,WAAW,MAAM,IAAI,YAAY;GACjD,MAAM,cAAc,MAAM,KAAK,0BAC7B,gBACA,KAAK,UACL,KACA,WACA,QACD;AAED,mBAAgB,KAAK;IACnB,MAAM;IACN,SAAS,wBAAS,QAAQ,KAAK,EAAE,YAAY,CAAC,QAC5C,SACA,WACD;IACD,kBAAkB,UAAU,oBAAoB,CAAE;IAClD,SAAS,UAAU;IACnB,YAAY,UAAU;IACtB,aAAa,MAAM,UAAU,gBAAgB;GAC9C,EAAC;AAEF,UAAO,KAAK,gCAAgC,IAAI,EAAE;EACnD;AAED,SAAO;CACR;CAED,MAAc,0BACZF,WACAG,YACAC,YACAC,aACAP,SACiB;EACjB,MAAM,mBAAmB,EAAE,WAAW;EACtC,MAAM,cAAc,oBAAK,WAAW,gBAAgB;EAEpD,MAAM,eAAe,wBAAS,uBAAQ,YAAY,EAAE,WAAW;EAC/D,MAAM,aAAa,aAAa,QAAQ,SAAS,MAAM;EAEvD,MAAM,wBAAwB,wBAC5B,uBAAQ,YAAY,EACpB,QAAQ,cACT;EAED,MAAM,WAAW;WACV,WAAW,WAAW,WAAW;SACnC,QAAQ,uBAAuB,SAAS,sBAAsB;;qDAElB,WAAW;;;;AAK5D,QAAM,gCAAU,aAAa,QAAQ;AACrC,SAAO;CACR;CAED,MAAc,8BACZE,WACAM,aACiB;AAEjB,QAAM,4BAAM,WAAW,EAAE,WAAW,KAAM,EAAC;EAE3C,MAAM,sBAAsB;EAC5B,MAAM,kBAAkB,oBAAK,WAAW,oBAAoB;EAG5D,MAAM,gCAAgB,IAAI;AAE1B,OAAK,MAAM,EAAE,MAAM,KAAK,IAAI,aAAa;GACvC,MAAM,eAAe,wBAAS,uBAAQ,gBAAgB,EAAE,KAAK,SAAS;GACtE,MAAM,aAAa,aAAa,QAAQ,SAAS,MAAM;AAEvD,QAAK,cAAc,IAAI,WAAW,CAChC,eAAc,IAAI,YAAY,CAAE,EAAC;AAEnC,iBAAc,IAAI,WAAW,CAAE,KAAK,IAAI;EACzC;EAGD,MAAM,UAAU,MAAM,KAAK,cAAc,SAAS,CAAC,CAChD,IACC,CAAC,CAAC,YAAYC,UAAQ,MACnB,WAAW,UAAQ,KAAK,KAAK,CAAC,WAAW,WAAW,IACxD,CACA,KAAK,KAAK;EAEb,MAAM,iBAAiB,YAAY,IAAI,CAAC,EAAE,KAAK,KAAK,IAAI;EAExD,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BnB,QAAQ;;;IAGN,eAAe,KAAK,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+F7B,QAAM,gCAAU,iBAAiB,QAAQ;AACzC,SAAO;CACR;AACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SubscriberInfo } from "./types-DB99_qIy.cjs";
|
|
2
|
+
import { BuildContext } from "./types-DLFN49M3.cjs";
|
|
3
|
+
import { ConstructGenerator, GeneratedConstruct, GeneratorOptions } from "./Generator-DxQMCQp7.cjs";
|
|
4
|
+
import { Subscriber } from "@geekmidas/constructs/subscribers";
|
|
5
|
+
|
|
6
|
+
//#region src/generators/SubscriberGenerator.d.ts
|
|
7
|
+
declare class SubscriberGenerator extends ConstructGenerator<Subscriber<any, any, any, any, any, any>, SubscriberInfo[]> {
|
|
8
|
+
isConstruct(value: any): value is Subscriber<any, any, any, any, any, any>;
|
|
9
|
+
build(context: BuildContext, constructs: GeneratedConstruct<Subscriber<any, any, any, any, any, any>>[], outputDir: string, options?: GeneratorOptions): Promise<SubscriberInfo[]>;
|
|
10
|
+
private generateSubscriberHandler;
|
|
11
|
+
private generateServerSubscribersFile;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { SubscriberGenerator };
|
|
15
|
+
//# sourceMappingURL=SubscriberGenerator-Dtb3HS4i.d.cts.map
|