opinionated-machine 6.16.0 → 6.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +319 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +2 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/AbstractController.d.ts +21 -0
  6. package/dist/lib/AbstractController.js +20 -0
  7. package/dist/lib/AbstractController.js.map +1 -1
  8. package/dist/lib/DIContext.d.ts +25 -0
  9. package/dist/lib/DIContext.js +40 -3
  10. package/dist/lib/DIContext.js.map +1 -1
  11. package/dist/lib/api-contracts/AbstractApiController.d.ts +9 -0
  12. package/dist/lib/api-contracts/AbstractApiController.js +8 -0
  13. package/dist/lib/api-contracts/AbstractApiController.js.map +1 -1
  14. package/dist/lib/api-contracts/apiRouteBuilder.js +12 -4
  15. package/dist/lib/api-contracts/apiRouteBuilder.js.map +1 -1
  16. package/dist/lib/gateway/fastifyGatewayPlugin.d.ts +38 -0
  17. package/dist/lib/gateway/fastifyGatewayPlugin.js +36 -0
  18. package/dist/lib/gateway/fastifyGatewayPlugin.js.map +1 -0
  19. package/dist/lib/gateway/gatewayMetadata.d.ts +176 -0
  20. package/dist/lib/gateway/gatewayMetadata.js +168 -0
  21. package/dist/lib/gateway/gatewayMetadata.js.map +1 -0
  22. package/dist/lib/gateway/gatewaySymbol.d.ts +9 -0
  23. package/dist/lib/gateway/gatewaySymbol.js +10 -0
  24. package/dist/lib/gateway/gatewaySymbol.js.map +1 -0
  25. package/dist/lib/gateway/gatewayTypes.d.ts +59 -0
  26. package/dist/lib/gateway/gatewayTypes.js +2 -0
  27. package/dist/lib/gateway/gatewayTypes.js.map +1 -0
  28. package/dist/lib/gateway/index.d.ts +8 -0
  29. package/dist/lib/gateway/index.js +8 -0
  30. package/dist/lib/gateway/index.js.map +1 -0
  31. package/dist/lib/gateway/manifest/buildManifest.d.ts +38 -0
  32. package/dist/lib/gateway/manifest/buildManifest.js +90 -0
  33. package/dist/lib/gateway/manifest/buildManifest.js.map +1 -0
  34. package/dist/lib/gateway/manifest/manifestSchema.d.ts +321 -0
  35. package/dist/lib/gateway/manifest/manifestSchema.js +29 -0
  36. package/dist/lib/gateway/manifest/manifestSchema.js.map +1 -0
  37. package/dist/lib/gateway/manifest/pathNormalize.d.ts +22 -0
  38. package/dist/lib/gateway/manifest/pathNormalize.js +43 -0
  39. package/dist/lib/gateway/manifest/pathNormalize.js.map +1 -0
  40. package/dist/lib/gateway/withGatewayMetadata.d.ts +43 -0
  41. package/dist/lib/gateway/withGatewayMetadata.js +55 -0
  42. package/dist/lib/gateway/withGatewayMetadata.js.map +1 -0
  43. package/package.json +2 -1
package/README.md CHANGED
@@ -84,6 +84,15 @@ Very opinionated DI framework for fastify, built on top of awilix
84
84
  - [Registering Dual-Mode Controllers](#registering-dual-mode-controllers)
85
85
  - [Accept Header Routing](#accept-header-routing)
86
86
  - [Testing Dual-Mode Controllers](#testing-dual-mode-controllers)
87
+ - [Gateway Configuration](#gateway-configuration)
88
+ - [Quick Start](#quick-start)
89
+ - [Annotating Routes](#annotating-routes)
90
+ - [Avoiding Repetition With Defaults](#avoiding-repetition-with-defaults)
91
+ - [Type-Safe Matching](#type-safe-matching)
92
+ - [Field Reference](#field-reference)
93
+ - [Generating Gateway Configs](#generating-gateway-configs)
94
+ - [Inspecting the Manifest at Runtime](#inspecting-the-manifest-at-runtime)
95
+ - [What's Not Covered](#whats-not-covered)
87
96
 
88
97
  ## Basic usage
89
98
 
@@ -2799,3 +2808,313 @@ describe('DashboardDualModeController', () => {
2799
2808
  })
2800
2809
  })
2801
2810
 
2811
+ ## Gateway Configuration
2812
+
2813
+ Most services keep two copies of every route's policy: one in code, another in
2814
+ a hand-edited Envoy / KrakenD / Kong config. They drift, and outages happen at
2815
+ the seam. This feature lets you declare routing policy — timeouts, retries,
2816
+ rate limits, CORS, JWT auth, caching, header transforms, traffic matching —
2817
+ **next to the controller route it applies to**, then generate the gateway
2818
+ config from a single source of truth.
2819
+
2820
+ Generators ship as separate npm packages so your service binary doesn't pull
2821
+ them in:
2822
+
2823
+ | Gateway | Package | Output |
2824
+ | ------- | ------- | ------ |
2825
+ | Envoy | [`@opinionated-machine/gateway-envoy`](./packages/gateway-envoy) | static v3 YAML/JSON |
2826
+ | KrakenD | [`@opinionated-machine/gateway-krakend`](./packages/gateway-krakend) | declarative v3 JSON |
2827
+ | Kong | [`@opinionated-machine/gateway-kong`](./packages/gateway-kong) | DB-less declarative YAML/JSON |
2828
+
2829
+ ### Quick Start
2830
+
2831
+ A complete round-trip in two steps. First, annotate routes in your existing
2832
+ controller:
2833
+
2834
+ ```ts
2835
+ import { buildRestContract } from '@lokalise/api-contracts'
2836
+ import { buildFastifyRoute } from '@lokalise/fastify-api-contracts'
2837
+ import {
2838
+ AbstractController,
2839
+ type BuildRoutesReturnType,
2840
+ type GatewayMetadataValue,
2841
+ withGatewayMetadata,
2842
+ } from 'opinionated-machine'
2843
+ import { z } from 'zod/v4'
2844
+
2845
+ const getUser = buildRestContract({
2846
+ method: 'get',
2847
+ successResponseBodySchema: z.object({ id: z.string() }),
2848
+ requestPathParamsSchema: z.object({ userId: z.string() }),
2849
+ pathResolver: (p) => `/users/${p.userId}`,
2850
+ })
2851
+ const createUser = buildRestContract({
2852
+ method: 'post',
2853
+ requestBodySchema: z.object({ name: z.string() }),
2854
+ successResponseBodySchema: z.object({ id: z.string() }),
2855
+ pathResolver: () => '/users',
2856
+ })
2857
+
2858
+ export class UsersController extends AbstractController<typeof UsersController.contracts> {
2859
+ static readonly contracts = { getUser, createUser } as const
2860
+
2861
+ // Applies to every route in this controller; routes can override.
2862
+ override readonly gatewayDefaults: GatewayMetadataValue = {
2863
+ upstream: 'users-service',
2864
+ timeouts: { request: '5s' },
2865
+ auth: { required: true },
2866
+ }
2867
+
2868
+ private getUser = buildFastifyRoute(UsersController.contracts.getUser, async (req, reply) => { /* … */ })
2869
+ private createUser = buildFastifyRoute(UsersController.contracts.createUser, async (req, reply) => { /* … */ })
2870
+
2871
+ buildRoutes(): BuildRoutesReturnType<typeof UsersController.contracts> {
2872
+ return {
2873
+ getUser: withGatewayMetadata(UsersController.contracts.getUser, this.getUser, {
2874
+ cache: { ttl: '60s' },
2875
+ }),
2876
+ createUser: withGatewayMetadata(UsersController.contracts.createUser, this.createUser, {
2877
+ rateLimit: { requests: 10, per: '1m', key: 'ip' },
2878
+ }),
2879
+ }
2880
+ }
2881
+ }
2882
+ ```
2883
+
2884
+ Then write a small script that turns the running service definition into a
2885
+ gateway config — wire it into your build / CI pipeline:
2886
+
2887
+ ```ts
2888
+ // bin/render-envoy.ts
2889
+ import { writeFileSync } from 'node:fs'
2890
+ import { renderEnvoyConfig } from '@opinionated-machine/gateway-envoy'
2891
+ import { buildContext } from '../src/diContext.ts' // your DIContext factory
2892
+
2893
+ const ctx = await buildContext()
2894
+ const manifest = ctx.buildGatewayManifest({
2895
+ service: 'users-api',
2896
+ defaults: { cors: { origins: ['https://app.example.com'], credentials: true } },
2897
+ })
2898
+
2899
+ const { yaml, warnings } = renderEnvoyConfig(manifest, {
2900
+ listenPort: 8080,
2901
+ clusters: { 'users-service': { hosts: ['users:8081'] } },
2902
+ })
2903
+
2904
+ writeFileSync('envoy.yaml', yaml)
2905
+ if (warnings.length) console.warn('[envoy]', warnings)
2906
+ ```
2907
+
2908
+ ```sh
2909
+ $ tsx bin/render-envoy.ts && envoy --mode validate -c envoy.yaml
2910
+ configuration 'envoy.yaml' OK
2911
+ ```
2912
+
2913
+ The rest of this section unpacks each piece in detail.
2914
+
2915
+ ### Annotating Routes
2916
+
2917
+ `withGatewayMetadata(contract, route, metadata)` takes:
2918
+
2919
+ - the **contract** — used purely for type inference on `match.headers`,
2920
+ `match.query` and `rateLimit.key`,
2921
+ - the **route** built by `buildFastifyRoute(...)` (or `buildApiRoute(...)`),
2922
+ - the **metadata** — see [Field Reference](#field-reference).
2923
+
2924
+ Apply it inside `buildRoutes()` (or in the `routes` array for
2925
+ `AbstractApiController`) so every route's gateway policy is in one scannable
2926
+ block. Annotated and un-annotated routes mix freely, and your existing
2927
+ `buildFastifyRoute(...)` calls don't change at all:
2928
+
2929
+ ```ts
2930
+ buildRoutes() {
2931
+ return {
2932
+ getUser: withGatewayMetadata(c.getUser, this.getUser, { cache: { ttl: '60s' } }),
2933
+ createUser: withGatewayMetadata(c.createUser, this.createUser, { rateLimit: { requests: 10, per: '1m', key: 'ip' } }),
2934
+ deleteUser: this.deleteUser, // no per-route policy; inherits defaults
2935
+ }
2936
+ }
2937
+ ```
2938
+
2939
+ For api-contract controllers, drop it directly into `routes`:
2940
+
2941
+ ```ts
2942
+ class UsersApiController extends AbstractApiController {
2943
+ readonly routes = [
2944
+ withGatewayMetadata(getUser, buildApiRoute(getUser, async (req) => /* … */), { cache: { ttl: '60s' } }),
2945
+ buildApiRoute(deleteUser, async (req) => /* … */),
2946
+ ]
2947
+ }
2948
+ ```
2949
+
2950
+ Annotations are invisible to Fastify — adding them never changes runtime
2951
+ behaviour, so you can introduce them gradually on an existing service.
2952
+
2953
+ ### Avoiding Repetition With Defaults
2954
+
2955
+ Most fields you'd write per route — upstream, base timeouts, auth posture,
2956
+ shared tags — are the same across every route in a controller, or every route
2957
+ in a service. Declare them once:
2958
+
2959
+ | Layer | Where | When to use |
2960
+ | ----- | ----- | ----------- |
2961
+ | Service-wide | `buildGatewayManifest({ defaults: … })` | Cross-cutting policy: CORS, idle timeouts, observability tags |
2962
+ | Controller | `override readonly gatewayDefaults = { … }` | Per-controller upstream, auth posture, base timeouts |
2963
+ | Per-route | `withGatewayMetadata(...)` | Anything specific to one endpoint |
2964
+
2965
+ Layers deep-merge in that order: service → controller → route. **Arrays in
2966
+ later layers replace** (not append), which keeps `weights`, `tags`, and
2967
+ `match.headers` predictable.
2968
+
2969
+ ```ts
2970
+ context.buildGatewayManifest({
2971
+ service: 'users-api',
2972
+ defaults: {
2973
+ timeouts: { idle: '60s', connect: '1s' },
2974
+ cors: { origins: ['https://app.example.com'], credentials: true },
2975
+ tags: ['users-api'],
2976
+ },
2977
+ })
2978
+ ```
2979
+
2980
+ ### Type-Safe Matching
2981
+
2982
+ `match.headers` and `match.query` keys are inferred from the contract's
2983
+ `requestHeaderSchema` / `requestQuerySchema`. Typos and stale references
2984
+ become compile errors before you ever ship a config:
2985
+
2986
+ ```ts
2987
+ const getUser = buildRestContract({
2988
+ method: 'get',
2989
+ successResponseBodySchema: ResponseBody,
2990
+ requestHeaderSchema: z.object({ 'x-trace-id': z.string() }),
2991
+ requestPathParamsSchema: z.object({ userId: z.string() }),
2992
+ pathResolver: (p) => `/users/${p.userId}`,
2993
+ })
2994
+
2995
+ withGatewayMetadata(getUser, this.getUser, {
2996
+ match: {
2997
+ headers: {
2998
+ 'x-trace-id': { regex: '^[a-f0-9]+$' }, // ✅ type-checked against the contract
2999
+ 'x-typo': 'foo', // ❌ compile error
3000
+ },
3001
+ customHeaders: {
3002
+ 'x-cf-tenant': 'enterprise', // ✅ explicit escape hatch for headers not in the contract
3003
+ },
3004
+ },
3005
+ })
3006
+ ```
3007
+
3008
+ `rateLimit.key` narrows the same way — `{ header: 'x-trace-id' }` only works
3009
+ if `'x-trace-id'` is in `requestHeaderSchema`; otherwise use
3010
+ `{ customHeader: '…' }`.
3011
+
3012
+ ### Field Reference
3013
+
3014
+ Every field is optional. The shapes below cover the common cases — see
3015
+ [`gatewayMetadata.ts`](./lib/gateway/gatewayMetadata.ts) for the complete Zod
3016
+ schema, which is also what produces precise validation errors at generation
3017
+ time.
3018
+
3019
+ | Field | Example | Notes |
3020
+ | ----- | ------- | ----- |
3021
+ | `upstream` | `'users-service'` | Logical cluster name; resolved to a host by the generator |
3022
+ | `timeouts` | `{ request: '5s', idle: '60s', connect: '1s' }` | Duration units: `ms` / `s` / `m` / `h` |
3023
+ | `retry` | `{ attempts: 2, on: ['5xx', 'connect-failure'], perTryTimeout: '2s' }` | |
3024
+ | `rateLimit` | `{ requests: 100, per: '1m', key: 'ip' }` | `key`: `'ip'`, `{ header }`, `{ customHeader }`, `{ query }`, `{ customQuery }` |
3025
+ | `cache` | `{ ttl: '60s', methods: ['GET'], vary: ['Accept-Language'] }` | |
3026
+ | `cors` | `{ origins: ['https://app.example.com'], credentials: true }` | |
3027
+ | `auth` | `{ required: true, jwt: { issuer: '…', audiences: ['…'], jwksUri: '…' } }` | |
3028
+ | `circuitBreaker` | `{ maxRequests: 100, maxRetries: 3 }` | |
3029
+ | `match` | `{ headers, customHeaders, query, customQuery, host }` | Rule values: bare string (exact), `{ exact }`, `{ prefix }`, `{ regex }` |
3030
+ | `rewrite` | `{ stripPrefix: '/v2' }` or `{ replacePrefix: { from: '/v1', to: '/v2' } }` | |
3031
+ | `traffic` | `{ weights: [{ upstream: 'a', weight: 80 }, { upstream: 'b', weight: 20 }] }` | Also `shadow: { upstream, percent }` |
3032
+ | `headers` | `{ request: { add: { 'x-internal': 'true' }, remove: ['cookie'] }, response: … }` | Free-form keys; typically infra headers not in the contract |
3033
+ | `tags`, `visibility` | `tags: ['users']`, `visibility: 'internal'` | Documentation / partitioning |
3034
+ | `extensions` | `{ envoy: { … }, krakend: { … }, kong: { … } }` | Vendor escape hatch; merged onto the generated route last |
3035
+
3036
+ ### Generating Gateway Configs
3037
+
3038
+ Each generator is a pure function — manifest in, config out — so you typically
3039
+ call them from a small build-time script. Pick one or all:
3040
+
3041
+ ```ts
3042
+ import { writeFileSync } from 'node:fs'
3043
+ import { renderEnvoyConfig } from '@opinionated-machine/gateway-envoy'
3044
+ import { renderKrakendConfig } from '@opinionated-machine/gateway-krakend'
3045
+ import { renderKongConfig } from '@opinionated-machine/gateway-kong'
3046
+
3047
+ const manifest = context.buildGatewayManifest({ service: 'users-api' })
3048
+
3049
+ writeFileSync('envoy.yaml',
3050
+ renderEnvoyConfig(manifest, {
3051
+ listenPort: 8080,
3052
+ clusters: { 'users-service': { hosts: ['users:8081'] } },
3053
+ }).yaml)
3054
+
3055
+ writeFileSync('krakend.json',
3056
+ JSON.stringify(renderKrakendConfig(manifest, {
3057
+ port: 8080,
3058
+ upstreams: { 'users-service': 'http://users:8081' },
3059
+ }).json, null, 2))
3060
+
3061
+ writeFileSync('kong.yaml',
3062
+ renderKongConfig(manifest, {
3063
+ upstreams: { 'users-service': { url: 'http://users:8081' } },
3064
+ }).yaml)
3065
+ ```
3066
+
3067
+ Each result includes `warnings: string[]` listing metadata fields the gateway
3068
+ can't natively express — log them so policy isn't silently dropped (e.g. Envoy
3069
+ doesn't ship an HTTP cache filter, so `cache.ttl` will appear in
3070
+ `warnings` under the Envoy generator). When you need a knob the universal
3071
+ model doesn't cover, hand-write it under `extensions.<vendor>` on the route —
3072
+ generators merge that block onto the rendered route last.
3073
+
3074
+ For each gateway's full mapping table and quirks:
3075
+
3076
+ - [`@opinionated-machine/gateway-envoy`](./packages/gateway-envoy/README.md)
3077
+ - [`@opinionated-machine/gateway-krakend`](./packages/gateway-krakend/README.md)
3078
+ - [`@opinionated-machine/gateway-kong`](./packages/gateway-kong/README.md)
3079
+
3080
+ ### Inspecting the Manifest at Runtime
3081
+
3082
+ When you want the manifest from outside Node — a deployment CLI written in
3083
+ another language, an ops dashboard, a debug-time `curl` — register
3084
+ `fastifyGatewayPlugin`. The running service then exposes its manifest both in
3085
+ code and over HTTP:
3086
+
3087
+ ```ts
3088
+ import { fastifyGatewayPlugin } from 'opinionated-machine'
3089
+
3090
+ await app.register(fastifyGatewayPlugin, {
3091
+ context, // your DIContext
3092
+ defaults: { service: 'users-api' }, // service name + any service-wide defaults
3093
+ // exposeRoute: '/__gateway/manifest', // opt-in HTTP route; omit to keep the manifest in-process only
3094
+ })
3095
+
3096
+ // In code, e.g. in another plugin or a graceful-shutdown drain hook:
3097
+ const manifest = app.buildGatewayManifest()
3098
+
3099
+ // Optionally fetch over HTTP from a CLI / sibling process — only when you
3100
+ // set `exposeRoute` above. The plugin never registers an HTTP route by
3101
+ // default to avoid leaking internal routing topology to unauthenticated
3102
+ // callers; pair it with auth middleware appropriate for your service.
3103
+ // curl http://localhost:8080/__gateway/manifest | jq '.routes'
3104
+ ```
3105
+
3106
+ The manifest is rebuilt on every call, so it always reflects the current set
3107
+ of registered controllers.
3108
+
3109
+ ### What's Not Covered
3110
+
3111
+ - **SSE and dual-mode controllers.** Only routes from `AbstractController` and
3112
+ `AbstractApiController` appear in the manifest today. Streaming routes still
3113
+ proxy through every gateway, but they aren't listed.
3114
+ - **Fields a particular gateway can't natively express.** They show up in
3115
+ `result.warnings` rather than disappearing. Reach for `extensions.<vendor>`
3116
+ to hand-write the missing piece on a per-route basis.
3117
+ - **Runtime drift detection.** The manifest is built from your code; the
3118
+ gateway runs separately. The generators don't compare deployed gateway
3119
+ state against the manifest.
3120
+
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export type { NestedPartial } from './lib/configUtils.js';
6
6
  export { type DependencyInjectionOptions, DIContext, type RegisterDependenciesParams, } from './lib/DIContext.js';
7
7
  export { ENABLE_ALL, isAnyMessageQueueConsumerEnabled, isEnqueuedJobWorkersEnabled, isJobQueueEnabled, isMessageQueueConsumerEnabled, isPeriodicJobEnabled, resolveJobQueuesEnabled, } from './lib/diConfigUtils.js';
8
8
  export * from './lib/dualmode/index.js';
9
+ export * from './lib/gateway/index.js';
9
10
  export * from './lib/resolverFunctions.js';
10
11
  export * from './lib/routes/index.js';
11
12
  export * from './lib/sse/index.js';
package/dist/index.js CHANGED
@@ -6,6 +6,8 @@ export { DIContext, } from './lib/DIContext.js';
6
6
  export { ENABLE_ALL, isAnyMessageQueueConsumerEnabled, isEnqueuedJobWorkersEnabled, isJobQueueEnabled, isMessageQueueConsumerEnabled, isPeriodicJobEnabled, resolveJobQueuesEnabled, } from './lib/diConfigUtils.js';
7
7
  // Dual-mode (SSE + JSON)
8
8
  export * from './lib/dualmode/index.js';
9
+ // Gateway metadata & manifest
10
+ export * from './lib/gateway/index.js';
9
11
  export * from './lib/resolverFunctions.js';
10
12
  // Routes (unified route builder)
11
13
  export * from './lib/routes/index.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA8B,MAAM,6BAA6B,CAAA;AAC5F,OAAO,EACL,cAAc,GAKf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,0BAA0B,GAE3B,MAAM,qCAAqC,CAAA;AAC5C,cAAc,8BAA8B,CAAA;AAE5C,OAAO,EAEL,SAAS,GAEV,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,UAAU,EACV,gCAAgC,EAChC,2BAA2B,EAC3B,iBAAiB,EACjB,6BAA6B,EAC7B,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,wBAAwB,CAAA;AAC/B,yBAAyB;AACzB,cAAc,yBAAyB,CAAA;AACvC,cAAc,4BAA4B,CAAA;AAC1C,iCAAiC;AACjC,cAAc,uBAAuB,CAAA;AACrC,MAAM;AACN,cAAc,oBAAoB,CAAA;AAClC,wBAAwB;AACxB,cAAc,wBAAwB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA8B,MAAM,6BAA6B,CAAA;AAC5F,OAAO,EACL,cAAc,GAKf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,0BAA0B,GAE3B,MAAM,qCAAqC,CAAA;AAC5C,cAAc,8BAA8B,CAAA;AAE5C,OAAO,EAEL,SAAS,GAEV,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,UAAU,EACV,gCAAgC,EAChC,2BAA2B,EAC3B,iBAAiB,EACjB,6BAA6B,EAC7B,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,wBAAwB,CAAA;AAC/B,yBAAyB;AACzB,cAAc,yBAAyB,CAAA;AACvC,8BAA8B;AAC9B,cAAc,wBAAwB,CAAA;AACtC,cAAc,4BAA4B,CAAA;AAC1C,iCAAiC;AACjC,cAAc,uBAAuB,CAAA;AACrC,MAAM;AACN,cAAc,oBAAoB,CAAA;AAClC,wBAAwB;AACxB,cAAc,wBAAwB,CAAA"}
@@ -1,6 +1,7 @@
1
1
  import type { CommonRouteDefinition, DeleteRouteDefinition, GetRouteDefinition, PayloadRouteDefinition } from '@lokalise/api-contracts';
2
2
  import type { buildFastifyRoute } from '@lokalise/fastify-api-contracts';
3
3
  import type { z } from 'zod/v4';
4
+ import type { GatewayMetadataValue } from './gateway/gatewayMetadata.ts';
4
5
  type AnyCommonRouteDefinition = CommonRouteDefinition<any, any, any, any, any, any, any, any>;
5
6
  type OptionalZodSchema = z.Schema | undefined;
6
7
  type FastifyPayloadRouteReturnType<RequestBody extends OptionalZodSchema, ResponseBody extends OptionalZodSchema, Path extends OptionalZodSchema, Query extends OptionalZodSchema, Headers extends OptionalZodSchema, ResponseHeaders extends OptionalZodSchema, IsNonJSONResponseExpected extends boolean, IsEmptyResponseExpected extends boolean, ResponseSchemasByStatusCode extends Record<number, any> | undefined> = ReturnType<typeof buildFastifyRoute<RequestBody, ResponseBody, Path, Query, Headers, ResponseHeaders, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>>;
@@ -10,5 +11,25 @@ export type BuildRoutesReturnType<APIContracts extends Record<string, AnyCommonR
10
11
  };
11
12
  export declare abstract class AbstractController<APIContracts extends Record<string, AnyCommonRouteDefinition>> {
12
13
  abstract buildRoutes(): BuildRoutesReturnType<APIContracts>;
14
+ /**
15
+ * Optional controller-level defaults for gateway metadata.
16
+ *
17
+ * Merged underneath per-route metadata when `DIContext.buildGatewayManifest()`
18
+ * assembles a manifest. Use this for fields that apply to every route in the
19
+ * controller (e.g. `upstream`, `auth`, baseline `timeouts`).
20
+ *
21
+ * Service-wide defaults (passed to `buildGatewayManifest({ defaults })`) sit
22
+ * underneath these.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * public readonly gatewayDefaults: GatewayMetadataValue = {
27
+ * upstream: 'users-service',
28
+ * timeouts: { request: '5s' },
29
+ * auth: { required: true },
30
+ * }
31
+ * ```
32
+ */
33
+ readonly gatewayDefaults?: GatewayMetadataValue;
13
34
  }
14
35
  export {};
@@ -1,3 +1,23 @@
1
1
  export class AbstractController {
2
+ /**
3
+ * Optional controller-level defaults for gateway metadata.
4
+ *
5
+ * Merged underneath per-route metadata when `DIContext.buildGatewayManifest()`
6
+ * assembles a manifest. Use this for fields that apply to every route in the
7
+ * controller (e.g. `upstream`, `auth`, baseline `timeouts`).
8
+ *
9
+ * Service-wide defaults (passed to `buildGatewayManifest({ defaults })`) sit
10
+ * underneath these.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * public readonly gatewayDefaults: GatewayMetadataValue = {
15
+ * upstream: 'users-service',
16
+ * timeouts: { request: '5s' },
17
+ * auth: { required: true },
18
+ * }
19
+ * ```
20
+ */
21
+ gatewayDefaults;
2
22
  }
3
23
  //# sourceMappingURL=AbstractController.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractController.js","sourceRoot":"","sources":["../../lib/AbstractController.ts"],"names":[],"mappings":"AAsHA,MAAM,OAAgB,kBAAkB;CAIvC"}
1
+ {"version":3,"file":"AbstractController.js","sourceRoot":"","sources":["../../lib/AbstractController.ts"],"names":[],"mappings":"AAuHA,MAAM,OAAgB,kBAAkB;IAKtC;;;;;;;;;;;;;;;;;;OAkBG;IACa,eAAe,CAAuB;CACvD"}
@@ -4,6 +4,7 @@ import type { FastifyInstance } from 'fastify';
4
4
  import type { AbstractModule } from './AbstractModule.js';
5
5
  import { type NestedPartial } from './configUtils.js';
6
6
  import type { ENABLE_ALL } from './diConfigUtils.js';
7
+ import { type BuildGatewayManifestOptions, type GatewayManifest } from './gateway/index.js';
7
8
  import { type RegisterDualModeRoutesOptions, type RegisterSSERoutesOptions } from './routes/index.js';
8
9
  export type RegisterDependenciesParams<Dependencies, Config, ExternalDependencies> = {
9
10
  modules: readonly AbstractModule<unknown, ExternalDependencies>[];
@@ -38,6 +39,30 @@ export declare class DIContext<Dependencies extends object, Config extends objec
38
39
  private registerModule;
39
40
  registerDependencies(params: RegisterDependenciesParams<Dependencies, Config, ExternalDependencies>, externalDependencies: ExternalDependencies, resolveControllers?: boolean): void;
40
41
  registerRoutes(app: FastifyInstance<any, any, any, any>): void;
42
+ /**
43
+ * Build a vendor-neutral gateway manifest from all registered REST and
44
+ * api-contract controllers. Routes carrying gateway metadata (attached via
45
+ * `withGatewayMetadata()`) get that metadata merged with controller-level
46
+ * `gatewayDefaults` and the `defaults` passed here. Routes without any
47
+ * metadata still appear in the manifest with empty metadata.
48
+ *
49
+ * The returned object is JSON-serializable; pass it to a generator package
50
+ * like `@opinionated-machine/gateway-envoy` or
51
+ * `@opinionated-machine/gateway-krakend` to produce a config.
52
+ *
53
+ * SSE and dual-mode controllers are not included in v1.
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * const manifest = context.buildGatewayManifest({
58
+ * service: 'users-api',
59
+ * defaults: { cors: { origins: ['https://app.example.com'] } },
60
+ * })
61
+ * const envoy = renderEnvoyConfig(manifest, { listenPort: 8080, clusters: { 'users-service': { hosts: ['users:8081'] } } })
62
+ * writeFileSync('envoy.yaml', envoy.yaml)
63
+ * ```
64
+ */
65
+ buildGatewayManifest(options: BuildGatewayManifestOptions): GatewayManifest;
41
66
  /**
42
67
  * Check if any SSE controllers are registered.
43
68
  * Use this to conditionally call registerSSERoutes().
@@ -1,6 +1,7 @@
1
1
  import { AwilixManager } from 'awilix-manager';
2
2
  import { merge } from 'ts-deepmerge';
3
3
  import { mergeConfigAndDependencyOverrides } from './configUtils.js';
4
+ import { buildGatewayManifestFrom, } from './gateway/index.js';
4
5
  import { buildFastifyRoute, } from './routes/index.js';
5
6
  export class DIContext {
6
7
  options;
@@ -53,7 +54,7 @@ export class DIContext {
53
54
  targetDiConfig[name] = resolver;
54
55
  }
55
56
  else {
56
- this.controllerResolvers.push(resolver);
57
+ this.controllerResolvers.push({ name, resolver: resolver });
57
58
  }
58
59
  }
59
60
  }
@@ -99,9 +100,9 @@ export class DIContext {
99
100
  }
100
101
  // biome-ignore lint/suspicious/noExplicitAny: we don't care about what instance we get here
101
102
  registerRoutes(app) {
102
- for (const controllerResolver of this.controllerResolvers) {
103
+ for (const { resolver } of this.controllerResolvers) {
103
104
  // biome-ignore lint/suspicious/noExplicitAny: any controller works here
104
- const controller = controllerResolver.resolve(this.diContainer);
105
+ const controller = resolver.resolve(this.diContainer);
105
106
  const routes = controller.buildRoutes();
106
107
  for (const route of Object.values(routes)) {
107
108
  // Cast needed: GET/DELETE routes have body:undefined, POST/PATCH have body:unknown
@@ -116,6 +117,42 @@ export class DIContext {
116
117
  }
117
118
  }
118
119
  }
120
+ /**
121
+ * Build a vendor-neutral gateway manifest from all registered REST and
122
+ * api-contract controllers. Routes carrying gateway metadata (attached via
123
+ * `withGatewayMetadata()`) get that metadata merged with controller-level
124
+ * `gatewayDefaults` and the `defaults` passed here. Routes without any
125
+ * metadata still appear in the manifest with empty metadata.
126
+ *
127
+ * The returned object is JSON-serializable; pass it to a generator package
128
+ * like `@opinionated-machine/gateway-envoy` or
129
+ * `@opinionated-machine/gateway-krakend` to produce a config.
130
+ *
131
+ * SSE and dual-mode controllers are not included in v1.
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * const manifest = context.buildGatewayManifest({
136
+ * service: 'users-api',
137
+ * defaults: { cors: { origins: ['https://app.example.com'] } },
138
+ * })
139
+ * const envoy = renderEnvoyConfig(manifest, { listenPort: 8080, clusters: { 'users-service': { hosts: ['users:8081'] } } })
140
+ * writeFileSync('envoy.yaml', envoy.yaml)
141
+ * ```
142
+ */
143
+ buildGatewayManifest(options) {
144
+ const collected = [];
145
+ for (const { name, resolver } of this.controllerResolvers) {
146
+ // biome-ignore lint/suspicious/noExplicitAny: any controller works here
147
+ const controller = resolver.resolve(this.diContainer);
148
+ collected.push({ name, kind: 'rest', controller });
149
+ }
150
+ for (const name of this.apiControllerNames) {
151
+ const controller = this.diContainer.resolve(name);
152
+ collected.push({ name, kind: 'api', controller });
153
+ }
154
+ return buildGatewayManifestFrom(collected, options);
155
+ }
119
156
  /**
120
157
  * Check if any SSE controllers are registered.
121
158
  * Use this to conditionally call registerSSERoutes().
@@ -1 +1 @@
1
- {"version":3,"file":"DIContext.js","sourceRoot":"","sources":["../../lib/DIContext.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DIContext.js","sourceRoot":"","sources":["../../lib/DIContext.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import type { RouteOptions } from 'fastify';
2
+ import type { GatewayMetadataValue } from '../gateway/gatewayMetadata.ts';
2
3
  /**
3
4
  * Abstract base class for controllers that use the `ApiContract` API.
4
5
  *
@@ -16,4 +17,12 @@ import type { RouteOptions } from 'fastify';
16
17
  */
17
18
  export declare abstract class AbstractApiController {
18
19
  abstract readonly routes: RouteOptions[];
20
+ /**
21
+ * Optional controller-level defaults for gateway metadata.
22
+ *
23
+ * Merged underneath per-route metadata (attached via `withGatewayMetadata`)
24
+ * when `DIContext.buildGatewayManifest()` assembles a manifest. See
25
+ * `AbstractController.gatewayDefaults` for full semantics.
26
+ */
27
+ readonly gatewayDefaults?: GatewayMetadataValue;
19
28
  }
@@ -14,5 +14,13 @@
14
14
  * ```
15
15
  */
16
16
  export class AbstractApiController {
17
+ /**
18
+ * Optional controller-level defaults for gateway metadata.
19
+ *
20
+ * Merged underneath per-route metadata (attached via `withGatewayMetadata`)
21
+ * when `DIContext.buildGatewayManifest()` assembles a manifest. See
22
+ * `AbstractController.gatewayDefaults` for full semantics.
23
+ */
24
+ gatewayDefaults;
17
25
  }
18
26
  //# sourceMappingURL=AbstractApiController.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractApiController.js","sourceRoot":"","sources":["../../../lib/api-contracts/AbstractApiController.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAgB,qBAAqB;CAE1C"}
1
+ {"version":3,"file":"AbstractApiController.js","sourceRoot":"","sources":["../../../lib/api-contracts/AbstractApiController.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAgB,qBAAqB;IAGzC;;;;;;OAMG;IACa,eAAe,CAAuB;CACvD"}
@@ -49,7 +49,7 @@ function getSchemaForStatusCode(contract, status) {
49
49
  if (isSseResponse(anyResponse) ||
50
50
  isTextResponse(anyResponse) ||
51
51
  isBlobResponse(anyResponse)) {
52
- return null;
52
+ continue;
53
53
  }
54
54
  return anyResponse;
55
55
  }
@@ -265,6 +265,15 @@ request, reply) {
265
265
  // ============================================================================
266
266
  // Internal Helpers — Schema
267
267
  // ============================================================================
268
+ function buildResponseSchemas(contract) {
269
+ return Object.keys(contract.responsesByStatusCode).reduce((acc, statusCode) => {
270
+ const schema = getSchemaForStatusCode(contract, Number(statusCode));
271
+ if (schema) {
272
+ acc[Number(statusCode)] = schema;
273
+ }
274
+ return acc;
275
+ }, {});
276
+ }
268
277
  function buildBaseSchema(contract) {
269
278
  const schema = {};
270
279
  if (contract.requestPathParamsSchema)
@@ -273,11 +282,10 @@ function buildBaseSchema(contract) {
273
282
  schema.querystring = contract.requestQuerySchema;
274
283
  if (contract.requestHeaderSchema)
275
284
  schema.headers = contract.requestHeaderSchema;
276
- if ('requestBodySchema' in contract &&
277
- contract.requestBodySchema !== undefined &&
278
- contract.requestBodySchema !== ContractNoBody) {
285
+ if (contract.requestBodySchema !== undefined && contract.requestBodySchema !== ContractNoBody) {
279
286
  schema.body = contract.requestBodySchema;
280
287
  }
288
+ schema.response = buildResponseSchemas(contract);
281
289
  return schema;
282
290
  }
283
291
  // ============================================================================