opinionated-machine 6.16.1 → 6.18.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.
Files changed (41) 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 +43 -4
  10. package/dist/lib/DIContext.js.map +1 -1
  11. package/dist/lib/api-contracts/AbstractApiController.d.ts +29 -8
  12. package/dist/lib/api-contracts/AbstractApiController.js +25 -6
  13. package/dist/lib/api-contracts/AbstractApiController.js.map +1 -1
  14. package/dist/lib/gateway/fastifyGatewayPlugin.d.ts +38 -0
  15. package/dist/lib/gateway/fastifyGatewayPlugin.js +36 -0
  16. package/dist/lib/gateway/fastifyGatewayPlugin.js.map +1 -0
  17. package/dist/lib/gateway/gatewayMetadata.d.ts +176 -0
  18. package/dist/lib/gateway/gatewayMetadata.js +168 -0
  19. package/dist/lib/gateway/gatewayMetadata.js.map +1 -0
  20. package/dist/lib/gateway/gatewaySymbol.d.ts +9 -0
  21. package/dist/lib/gateway/gatewaySymbol.js +10 -0
  22. package/dist/lib/gateway/gatewaySymbol.js.map +1 -0
  23. package/dist/lib/gateway/gatewayTypes.d.ts +59 -0
  24. package/dist/lib/gateway/gatewayTypes.js +2 -0
  25. package/dist/lib/gateway/gatewayTypes.js.map +1 -0
  26. package/dist/lib/gateway/index.d.ts +8 -0
  27. package/dist/lib/gateway/index.js +8 -0
  28. package/dist/lib/gateway/index.js.map +1 -0
  29. package/dist/lib/gateway/manifest/buildManifest.d.ts +38 -0
  30. package/dist/lib/gateway/manifest/buildManifest.js +90 -0
  31. package/dist/lib/gateway/manifest/buildManifest.js.map +1 -0
  32. package/dist/lib/gateway/manifest/manifestSchema.d.ts +321 -0
  33. package/dist/lib/gateway/manifest/manifestSchema.js +29 -0
  34. package/dist/lib/gateway/manifest/manifestSchema.js.map +1 -0
  35. package/dist/lib/gateway/manifest/pathNormalize.d.ts +22 -0
  36. package/dist/lib/gateway/manifest/pathNormalize.js +43 -0
  37. package/dist/lib/gateway/manifest/pathNormalize.js.map +1 -0
  38. package/dist/lib/gateway/withGatewayMetadata.d.ts +43 -0
  39. package/dist/lib/gateway/withGatewayMetadata.js +55 -0
  40. package/dist/lib/gateway/withGatewayMetadata.js.map +1 -0
  41. 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
@@ -110,12 +111,50 @@ export class DIContext {
110
111
  }
111
112
  }
112
113
  for (const controllerName of this.apiControllerNames) {
114
+ // biome-ignore lint/suspicious/noExplicitAny: any api controllers works here
113
115
  const controller = this.diContainer.resolve(controllerName);
114
- for (const route of controller.routes) {
116
+ for (const route of Object.values(controller.routes)) {
115
117
  app.route(route);
116
118
  }
117
119
  }
118
120
  }
121
+ /**
122
+ * Build a vendor-neutral gateway manifest from all registered REST and
123
+ * api-contract controllers. Routes carrying gateway metadata (attached via
124
+ * `withGatewayMetadata()`) get that metadata merged with controller-level
125
+ * `gatewayDefaults` and the `defaults` passed here. Routes without any
126
+ * metadata still appear in the manifest with empty metadata.
127
+ *
128
+ * The returned object is JSON-serializable; pass it to a generator package
129
+ * like `@opinionated-machine/gateway-envoy` or
130
+ * `@opinionated-machine/gateway-krakend` to produce a config.
131
+ *
132
+ * SSE and dual-mode controllers are not included in v1.
133
+ *
134
+ * @example
135
+ * ```ts
136
+ * const manifest = context.buildGatewayManifest({
137
+ * service: 'users-api',
138
+ * defaults: { cors: { origins: ['https://app.example.com'] } },
139
+ * })
140
+ * const envoy = renderEnvoyConfig(manifest, { listenPort: 8080, clusters: { 'users-service': { hosts: ['users:8081'] } } })
141
+ * writeFileSync('envoy.yaml', envoy.yaml)
142
+ * ```
143
+ */
144
+ buildGatewayManifest(options) {
145
+ const collected = [];
146
+ for (const { name, resolver } of this.controllerResolvers) {
147
+ // biome-ignore lint/suspicious/noExplicitAny: any controller works here
148
+ const controller = resolver.resolve(this.diContainer);
149
+ collected.push({ name, kind: 'rest', controller });
150
+ }
151
+ for (const name of this.apiControllerNames) {
152
+ // biome-ignore lint/suspicious/noExplicitAny: any api controller works here
153
+ const controller = this.diContainer.resolve(name);
154
+ collected.push({ name, kind: 'api', controller });
155
+ }
156
+ return buildGatewayManifestFrom(collected, options);
157
+ }
119
158
  /**
120
159
  * Check if any SSE controllers are registered.
121
160
  * 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,19 +1,40 @@
1
+ import type { ApiContract } from '@lokalise/api-contracts';
1
2
  import type { RouteOptions } from 'fastify';
3
+ import type { GatewayMetadataValue } from '../gateway/gatewayMetadata.ts';
2
4
  /**
3
5
  * Abstract base class for controllers that use the `ApiContract` API.
4
6
  *
5
- * Concrete controllers declare a `routes` property built with `buildApiRoute()`.
7
+ * Concrete controllers declare a static `contracts` field and a `routes` object
8
+ * built with `buildApiRoute()`. The generic ensures every contract has a matching route.
6
9
  *
7
10
  * @example
8
11
  * ```typescript
9
- * class UserController extends AbstractApiController {
10
- * readonly routes = [
11
- * buildApiRoute(getUser, async (req) => ({ status: 200, body: { id: req.params.id } })),
12
- * buildApiRoute(streamUpdates, async (_req, sse) => { sse.start('keepAlive') }),
13
- * ]
12
+ * class UserController extends AbstractApiController<typeof UserController.contracts> {
13
+ * static contracts = {
14
+ * getUser: getUserContract,
15
+ * streamUpdates: streamUpdatesContract,
16
+ * } as const
17
+ *
18
+ * readonly routes = {
19
+ * getUser: buildApiRoute(UserController.contracts.getUser, async (req) => ({
20
+ * status: 200,
21
+ * body: { id: req.params.id },
22
+ * })),
23
+ * streamUpdates: buildApiRoute(UserController.contracts.streamUpdates, async (_req, sse) => {
24
+ * sse.start('keepAlive')
25
+ * }),
26
+ * }
14
27
  * }
15
28
  * ```
16
29
  */
17
- export declare abstract class AbstractApiController {
18
- abstract readonly routes: RouteOptions[];
30
+ export declare abstract class AbstractApiController<APIContracts extends Record<string, ApiContract>> {
31
+ abstract readonly routes: Record<keyof APIContracts, RouteOptions>;
32
+ /**
33
+ * Optional controller-level defaults for gateway metadata.
34
+ *
35
+ * Merged underneath per-route metadata (attached via `withGatewayMetadata`)
36
+ * when `DIContext.buildGatewayManifest()` assembles a manifest. See
37
+ * `AbstractController.gatewayDefaults` for full semantics.
38
+ */
39
+ readonly gatewayDefaults?: GatewayMetadataValue;
19
40
  }