@travetto/web 7.0.0-rc.1 → 7.0.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,7 +30,7 @@ export class BaseWebMessage<B = unknown, C = unknown> implements WebMessage<B, C
30
30
  readonly context: C;
31
31
  readonly headers: WebHeaders;
32
32
  body?: B;
33
- constructor(o: WebMessageInit<B, C> = {});
33
+ constructor(input: WebMessageInit<B, C> = {});
34
34
  }
35
35
  ```
36
36
 
@@ -155,10 +155,10 @@ class SimpleController {
155
155
 
156
156
  ### Parameters
157
157
  Endpoints can be configured to describe and enforce parameter behavior. Request parameters can be defined in five areas:
158
- * [@PathParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L38) - Path params
159
- * [@QueryParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L45) - Query params - can be either a single value or bind to a whole object
160
- * [@Body](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L59) - Request body
161
- * [@HeaderParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L52) - Header values
158
+ * [@PathParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L36) - Path params
159
+ * [@QueryParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L43) - Query params - can be either a single value or bind to a whole object
160
+ * [@Body](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L57) - Request body
161
+ * [@HeaderParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L50) - Header values
162
162
 
163
163
  Each [@Param](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L16) can be configured to indicate:
164
164
  * `name` - Name of param, field name, defaults to handler parameter name if necessary
@@ -223,7 +223,7 @@ export class Simple {
223
223
  ```
224
224
 
225
225
  ### ContextParam
226
- In addition to endpoint parameters (i.e. user-provided inputs), there may also be a desire to access indirect contextual information. Specifically you may need access to the entire [WebRequest](https://github.com/travetto/travetto/tree/main/module/web/src/types/request.ts#L11). These are able to be injected using the [@ContextParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L66) on a class-level field from the [WebAsyncContext](https://github.com/travetto/travetto/tree/main/module/web/src/context.ts#L11). These are not exposed as endpoint parameters as they cannot be provided when making RPC invocations.
226
+ In addition to endpoint parameters (i.e. user-provided inputs), there may also be a desire to access indirect contextual information. Specifically you may need access to the entire [WebRequest](https://github.com/travetto/travetto/tree/main/module/web/src/types/request.ts#L11). These are able to be injected using the [@ContextParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L64) on a class-level field from the [WebAsyncContext](https://github.com/travetto/travetto/tree/main/module/web/src/context.ts#L11). These are not exposed as endpoint parameters as they cannot be provided when making RPC invocations.
227
227
 
228
228
  **Code: Example ContextParam usage**
229
229
  ```typescript
@@ -251,12 +251,12 @@ class ContextController {
251
251
  }
252
252
  ```
253
253
 
254
- **Note**: When referencing the [@ContextParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L66) values, the contract for idempotency needs to be carefully inspected, if expected. You can see in the example above that the [@CacheControl](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/common.ts#L45) decorator is used to ensure that the response is not cached.
254
+ **Note**: When referencing the [@ContextParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L64) values, the contract for idempotency needs to be carefully inspected, if expected. You can see in the example above that the [@CacheControl](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/common.ts#L45) decorator is used to ensure that the response is not cached.
255
255
 
256
256
  ### Validating Inputs
257
257
  The module provides high level access for [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding.") support, via decorators, for validating and typing request inputs.
258
258
 
259
- By default, all endpoint parameters are validated for type, and any additional constraints added (required, vs optional, minlength, etc). Each parameter location ([@PathParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L38), [@Body](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L59), [@QueryParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L45), [@HeaderParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L52)) primarily provides a source to bind the endpoint arguments from. Once bound, the module will validate that the provided arguments are in fact valid. All validation will occur before the endpoint is ever executed, ensuring a strong contract.
259
+ By default, all endpoint parameters are validated for type, and any additional constraints added (required, vs optional, minlength, etc). Each parameter location ([@PathParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L36), [@Body](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L57), [@QueryParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L43), [@HeaderParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L50)) primarily provides a source to bind the endpoint arguments from. Once bound, the module will validate that the provided arguments are in fact valid. All validation will occur before the endpoint is ever executed, ensuring a strong contract.
260
260
 
261
261
  **Code: Using Body for POST requests**
262
262
  ```typescript
@@ -667,7 +667,7 @@ export class AlowDenyController {
667
667
  }
668
668
 
669
669
  @Get('/raw')
670
- @ExcludeInterceptors(v => v.category === 'response')
670
+ @ExcludeInterceptors(({ category }) => category === 'response')
671
671
  withoutResponse(@QueryParam() value: string) {
672
672
 
673
673
  }
@@ -735,15 +735,15 @@ export class SimpleAuthInterceptor implements WebInterceptor {
735
735
  ```
736
736
 
737
737
  ## Cookie Support
738
- Cookies are a unique element, within the framework, as they sit on the request and response flows. Ideally we would separate these out, but given the support for key rotation, there is a scenario in which reading a cookie on the request, will result in a cookie needing to be written on the response. Because of this, cookies are treated as being outside the normal [WebRequest](https://github.com/travetto/travetto/tree/main/module/web/src/types/request.ts#L11) activity, and is exposed as the [@ContextParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L66) [CookieJar](https://github.com/travetto/travetto/tree/main/module/web/src/util/cookie.ts#L12). The [CookieJar](https://github.com/travetto/travetto/tree/main/module/web/src/util/cookie.ts#L12) has a fairly basic contract:
738
+ Cookies are a unique element, within the framework, as they sit on the request and response flows. Ideally we would separate these out, but given the support for key rotation, there is a scenario in which reading a cookie on the request, will result in a cookie needing to be written on the response. Because of this, cookies are treated as being outside the normal [WebRequest](https://github.com/travetto/travetto/tree/main/module/web/src/types/request.ts#L11) activity, and is exposed as the [@ContextParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L64) [CookieJar](https://github.com/travetto/travetto/tree/main/module/web/src/util/cookie.ts#L12). The [CookieJar](https://github.com/travetto/travetto/tree/main/module/web/src/util/cookie.ts#L12) has a fairly basic contract:
739
739
 
740
740
  **Code: CookieJar contract**
741
741
  ```typescript
742
742
  export class CookieJar {
743
743
  constructor({ keys, ...options }: CookieJarOptions = {});
744
744
  import(cookies: Cookie[]): this;
745
- has(name: string, opts: CookieGetOptions = {}): boolean;
746
- get(name: string, opts: CookieGetOptions = {}): string | undefined;
745
+ has(name: string, options: CookieGetOptions = {}): boolean;
746
+ get(name: string, options: CookieGetOptions = {}): string | undefined;
747
747
  set(cookie: Cookie): void;
748
748
  getAll(): Cookie[];
749
749
  importCookieHeader(header: string | null | undefined): this;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/web",
3
- "version": "7.0.0-rc.1",
3
+ "version": "7.0.0-rc.3",
4
4
  "description": "Declarative support for creating Web Applications",
5
5
  "keywords": [
6
6
  "web",
@@ -25,18 +25,18 @@
25
25
  "directory": "module/web"
26
26
  },
27
27
  "dependencies": {
28
- "@travetto/config": "^7.0.0-rc.0",
29
- "@travetto/context": "^7.0.0-rc.0",
30
- "@travetto/di": "^7.0.0-rc.0",
31
- "@travetto/registry": "^7.0.0-rc.0",
32
- "@travetto/runtime": "^7.0.0-rc.0",
33
- "@travetto/schema": "^7.0.0-rc.0",
28
+ "@travetto/config": "^7.0.0-rc.2",
29
+ "@travetto/context": "^7.0.0-rc.2",
30
+ "@travetto/di": "^7.0.0-rc.2",
31
+ "@travetto/registry": "^7.0.0-rc.2",
32
+ "@travetto/runtime": "^7.0.0-rc.2",
33
+ "@travetto/schema": "^7.0.0-rc.2",
34
34
  "find-my-way": "^9.3.0"
35
35
  },
36
36
  "peerDependencies": {
37
- "@travetto/cli": "^7.0.0-rc.0",
38
- "@travetto/test": "^7.0.0-rc.0",
39
- "@travetto/transformer": "^7.0.0-rc.0"
37
+ "@travetto/cli": "^7.0.0-rc.2",
38
+ "@travetto/test": "^7.0.0-rc.2",
39
+ "@travetto/transformer": "^7.0.0-rc.2"
40
40
  },
41
41
  "peerDependenciesMeta": {
42
42
  "@travetto/transformer": {
@@ -10,7 +10,7 @@ function isClass(target: unknown, property: unknown,): target is Class<unknown>
10
10
  }
11
11
 
12
12
  function register(config: Partial<EndpointConfig | ControllerConfig>): EndpointDecorator {
13
- return function <T>(instanceOrCls: ClassInstance | Class<T>, property?: string | symbol, _?: EndpointFunctionDescriptor) {
13
+ return function <T>(instanceOrCls: ClassInstance | Class<T>, property?: string, _?: EndpointFunctionDescriptor) {
14
14
  const adapter = ControllerRegistryIndex.getForRegister(getClass(instanceOrCls));
15
15
  if (isClass(instanceOrCls, property)) {
16
16
  adapter.register(config);
@@ -74,10 +74,10 @@ export function Accepts(types: [string, ...string[]]): EndpointDecorator {
74
74
  */
75
75
  export const ConfigureInterceptor = <T extends WebInterceptor>(
76
76
  cls: Class<T>,
77
- cfg: Partial<RetainPrimitiveFields<T['config']>>,
77
+ config: Partial<RetainPrimitiveFields<T['config']>>,
78
78
  extra?: Partial<EndpointConfig & ControllerConfig>
79
79
  ): EndpointDecorator =>
80
- ControllerRegistryIndex.createInterceptorConfigDecorator(cls, cfg, extra);
80
+ ControllerRegistryIndex.createInterceptorConfigDecorator(cls, config, extra);
81
81
 
82
82
  /**
83
83
  * Specifies if endpoint should be conditional
@@ -91,6 +91,6 @@ export function ConditionalRegister(handler: () => (boolean | Promise<boolean>))
91
91
  * Registers an interceptor exclusion filter
92
92
  * @kind decorator
93
93
  */
94
- export function ExcludeInterceptors(interceptorExclude: (val: WebInterceptor) => boolean): EndpointDecorator {
94
+ export function ExcludeInterceptors(interceptorExclude: (value: WebInterceptor) => boolean): EndpointDecorator {
95
95
  return register({ interceptorExclude });
96
96
  };
@@ -4,7 +4,7 @@ import { EndpointConfig, EndpointFunctionDescriptor } from '../registry/types.ts
4
4
  import { HTTP_METHODS, HttpMethod } from '../types/core.ts';
5
5
  import { ControllerRegistryIndex } from '../registry/registry-index.ts';
6
6
 
7
- type EndpointFunctionDecorator = <T>(instance: T, property: symbol | string, descriptor: EndpointFunctionDescriptor) => EndpointFunctionDescriptor;
7
+ type EndpointFunctionDecorator = <T>(instance: T, property: string, descriptor: EndpointFunctionDescriptor) => EndpointFunctionDescriptor;
8
8
 
9
9
  type EndpointDecConfig = Partial<EndpointConfig> & { path: string };
10
10
 
@@ -12,7 +12,7 @@ type EndpointDecConfig = Partial<EndpointConfig> & { path: string };
12
12
  * Generic Endpoint Decorator
13
13
  */
14
14
  export function Endpoint(config: EndpointDecConfig): EndpointFunctionDecorator {
15
- return function (instance: ClassInstance, property: symbol | string, descriptor: EndpointFunctionDescriptor): EndpointFunctionDescriptor {
15
+ return function (instance: ClassInstance, property: string, descriptor: EndpointFunctionDescriptor): EndpointFunctionDescriptor {
16
16
  ControllerRegistryIndex.getForRegister(getClass(instance)).registerEndpoint(property, { methodName: property }, config);
17
17
  return descriptor;
18
18
  };
@@ -25,9 +25,9 @@ function HttpEndpoint(method: HttpMethod, path: string): EndpointFunctionDecorat
25
25
  allowsBody,
26
26
  cacheable,
27
27
  httpMethod: method,
28
- responseFinalizer: v => {
29
- v.context.httpStatusCode ??= (v.body === null || v.body === undefined || v.body === '') ? emptyStatusCode : 200;
30
- return v;
28
+ responseFinalizer: value => {
29
+ value.context.httpStatusCode ??= (value.body === null || value.body === undefined || value.body === '') ? emptyStatusCode : 200;
30
+ return value;
31
31
  }
32
32
  });
33
33
  }
@@ -4,7 +4,7 @@ import { SchemaRegistryIndex } from '@travetto/schema';
4
4
  import { ControllerRegistryIndex } from '../registry/registry-index.ts';
5
5
  import { EndpointParameterConfig, EndpointParamLocation } from '../registry/types.ts';
6
6
 
7
- type ParamDecorator = (instance: ClassInstance, property: string | symbol, idx: number) => void;
7
+ type ParamDecorator = (instance: ClassInstance, property: string, idx: number) => void;
8
8
 
9
9
  /**
10
10
  * Define a parameter
@@ -13,50 +13,48 @@ type ParamDecorator = (instance: ClassInstance, property: string | symbol, idx:
13
13
  * @augments `@travetto/schema:Input`
14
14
  * @kind decorator
15
15
  */
16
- export function Param(location: EndpointParamLocation, extra: string | Partial<EndpointParameterConfig>): ParamDecorator {
17
- return (instance: ClassInstance, property: string | symbol, idx: number): void => {
18
- const name = typeof extra === 'string' ? extra : extra.name;
19
- const config = typeof extra === 'string' ? {} : extra;
20
-
21
- // Set name as needed
22
- if (name) {
23
- SchemaRegistryIndex.getForRegister(getClass(instance)).registerParameter(property, idx, { name });
16
+ export function Param(location: EndpointParamLocation, aliasOrConfig: string | Partial<EndpointParameterConfig>): ParamDecorator {
17
+ return (instance: ClassInstance, property: string, idx: number): void => {
18
+ const config = typeof aliasOrConfig === 'string' ? {} : aliasOrConfig;
19
+ const cls = getClass(instance);
20
+ if (typeof aliasOrConfig === 'string') {
21
+ SchemaRegistryIndex.getForRegister(cls).registerParameter(property, idx, {
22
+ aliases: [aliasOrConfig] // Register extra input string as an alias
23
+ });
24
24
  }
25
25
 
26
- ControllerRegistryIndex.getForRegister(getClass(instance)).registerEndpointParameter(property, idx, {
27
- index: idx, location, ...config
28
- });
26
+ ControllerRegistryIndex.getForRegister(cls).registerEndpointParameter(property, idx, { location, ...config });
29
27
  };
30
28
  }
31
29
 
32
30
  /**
33
31
  * Define a Path param
34
- * @param param The param configuration or name
32
+ * @input input The param configuration or alias
35
33
  * @augments `@travetto/schema:Input`
36
34
  * @kind decorator
37
35
  */
38
- export function PathParam(param: string | Partial<EndpointParameterConfig> = {}): ParamDecorator { return Param('path', param); }
36
+ export function PathParam(input: string | Partial<EndpointParameterConfig> = {}): ParamDecorator { return Param('path', input); }
39
37
  /**
40
38
  * Define a Query param
41
- * @param param The param configuration or name
39
+ * @input input The param configuration or alias
42
40
  * @augments `@travetto/schema:Input`
43
41
  * @kind decorator
44
42
  */
45
- export function QueryParam(param: string | Partial<EndpointParameterConfig> = {}): ParamDecorator { return Param('query', param); }
43
+ export function QueryParam(input: string | Partial<EndpointParameterConfig> = {}): ParamDecorator { return Param('query', input); }
46
44
  /**
47
45
  * Define a Header param
48
- * @param param The param configuration or name
46
+ * @input input The param configuration or alias
49
47
  * @augments `@travetto/schema:Input`
50
48
  * @kind decorator
51
49
  */
52
- export function HeaderParam(param: string | Partial<EndpointParameterConfig> = {}): ParamDecorator { return Param('header', param); }
50
+ export function HeaderParam(input: string | Partial<EndpointParameterConfig> = {}): ParamDecorator { return Param('header', input); }
53
51
  /**
54
52
  * Define a body param as an input
55
- * @param param The param configuration
53
+ * @input input The param configuration
56
54
  * @augments `@travetto/schema:Input`
57
55
  * @kind decorator
58
56
  */
59
- export function Body(param: Partial<EndpointParameterConfig> = {}): ParamDecorator { return Param('body', param); }
57
+ export function Body(input: Partial<EndpointParameterConfig> = {}): ParamDecorator { return Param('body', input); }
60
58
 
61
59
  /**
62
60
  * A contextual field as provided by the WebAsyncContext
@@ -64,7 +62,7 @@ export function Body(param: Partial<EndpointParameterConfig> = {}): ParamDecorat
64
62
  * @kind decorator
65
63
  */
66
64
  export function ContextParam() {
67
- return (instance: ClassInstance, property: string | symbol): void => {
65
+ return (instance: ClassInstance, property: string): void => {
68
66
  ControllerRegistryIndex.getForRegister(getClass(instance)).register({ contextParams: { [property]: true } });
69
67
  ControllerRegistryIndex.bindContextParamsOnPostConstruct(getClass(instance));
70
68
  };
@@ -56,14 +56,14 @@ export class AcceptInterceptor implements WebInterceptor<AcceptConfig> {
56
56
  }
57
57
 
58
58
  async filter({ request, config, next }: WebChainedContext<AcceptConfig>): Promise<WebResponse> {
59
- let res: WebResponse | undefined;
59
+ let response: WebResponse | undefined;
60
60
  try {
61
61
  this.validate(request, config);
62
- return res = await next();
63
- } catch (err) {
64
- throw res = await WebCommonUtil.catchResponse(err);
62
+ return response = await next();
63
+ } catch (error) {
64
+ throw response = await WebCommonUtil.catchResponse(error);
65
65
  } finally {
66
- res?.headers.setIfAbsent('Accept', config.types.join(','));
66
+ response?.headers.setIfAbsent('Accept', config.types.join(','));
67
67
  }
68
68
  }
69
69
  }
@@ -114,8 +114,8 @@ export class BodyInterceptor implements WebInterceptor<WebBodyConfig> {
114
114
  WebBodyUtil.parseBody(parserType, text);
115
115
 
116
116
  return next();
117
- } catch (err) {
118
- throw WebError.for('Malformed input', 400, { cause: err });
117
+ } catch (error) {
118
+ throw WebError.for('Malformed input', 400, { cause: error });
119
119
  }
120
120
  }
121
121
  }
@@ -81,8 +81,8 @@ export class CompressInterceptor implements WebInterceptor {
81
81
  return binaryResponse;
82
82
  }
83
83
 
84
- const opts = type === 'br' ? { params: { [constants.BROTLI_PARAM_QUALITY]: 4, ...raw.params }, ...raw } : { ...raw };
85
- const stream = COMPRESSORS[type](opts);
84
+ const options = type === 'br' ? { params: { [constants.BROTLI_PARAM_QUALITY]: 4, ...raw.params }, ...raw } : { ...raw };
85
+ const stream = COMPRESSORS[type](options);
86
86
 
87
87
  // If we are compressing
88
88
  binaryResponse.headers.set('Content-Encoding', type);
@@ -96,7 +96,7 @@ export class CookieInterceptor implements WebInterceptor<CookieConfig> {
96
96
  this.#cookieJar.set(jar);
97
97
 
98
98
  const response = await next();
99
- for (const c of jar.exportSetCookieHeader()) { response.headers.append('Set-Cookie', c); }
99
+ for (const cookie of jar.exportSetCookieHeader()) { response.headers.append('Set-Cookie', cookie); }
100
100
  return response;
101
101
  }
102
102
  }
@@ -72,13 +72,13 @@ export class CorsInterceptor implements WebInterceptor<CorsConfig> {
72
72
  decorate(request: WebRequest, resolved: CorsConfig['resolved'], response: WebResponse,): WebResponse {
73
73
  const origin = request.headers.get('Origin');
74
74
  if (resolved.origins.size === 0 || resolved.origins.has(origin!)) {
75
- for (const [k, v] of [
75
+ for (const [header, value] of [
76
76
  ['Access-Control-Allow-Origin', origin || '*'],
77
77
  ['Access-Control-Allow-Credentials', `${resolved.credentials}`],
78
78
  ['Access-Control-Allow-Methods', resolved.methods],
79
79
  ['Access-Control-Allow-Headers', resolved.headers || request.headers.get('Access-Control-Request-Headers') || '*'],
80
80
  ]) {
81
- response.headers.setIfAbsent(k, v);
81
+ response.headers.setIfAbsent(header, value);
82
82
  }
83
83
  }
84
84
  return response;
@@ -87,8 +87,8 @@ export class CorsInterceptor implements WebInterceptor<CorsConfig> {
87
87
  async filter({ request, config: { resolved }, next }: WebChainedContext<CorsConfig>): Promise<WebResponse> {
88
88
  try {
89
89
  return this.decorate(request, resolved, await next());
90
- } catch (err) {
91
- throw this.decorate(request, resolved, WebCommonUtil.catchResponse(err));
90
+ } catch (error) {
91
+ throw this.decorate(request, resolved, WebCommonUtil.catchResponse(error));
92
92
  }
93
93
  }
94
94
  }
@@ -41,8 +41,8 @@ export class LoggingInterceptor implements WebInterceptor {
41
41
  const response = await next();
42
42
  const duration = Date.now() - createdDate;
43
43
 
44
- const err = response.body instanceof Error ? response.body : undefined;
45
- const code = response.context.httpStatusCode ??= (!!err ? 500 : 200);
44
+ const error = response.body instanceof Error ? response.body : undefined;
45
+ const code = response.context.httpStatusCode ??= (!!error ? 500 : 200);
46
46
 
47
47
  const logMessage = {
48
48
  method: request.context.httpMethod,
@@ -61,8 +61,8 @@ export class LoggingInterceptor implements WebInterceptor {
61
61
  console.error('Request', logMessage);
62
62
  }
63
63
 
64
- if (this.config.showStackTrace && err) {
65
- console.error(err.message, { error: err });
64
+ if (this.config.showStackTrace && error) {
65
+ console.error(error.message, { error });
66
66
  }
67
67
 
68
68
  return response;
@@ -15,12 +15,12 @@ export class RespondInterceptor implements WebInterceptor {
15
15
  dependsOn = [LoggingInterceptor];
16
16
 
17
17
  async filter(ctx: WebChainedContext): Promise<WebResponse> {
18
- let res;
18
+ let response;
19
19
  try {
20
- res = await ctx.next();
21
- } catch (err) {
22
- res = WebCommonUtil.catchResponse(err);
20
+ response = await ctx.next();
21
+ } catch (error) {
22
+ response = WebCommonUtil.catchResponse(error);
23
23
  }
24
- return res;
24
+ return response;
25
25
  }
26
26
  }
@@ -30,7 +30,7 @@ function combineClassConfigs(base: ControllerConfig, ...overrides: Partial<Contr
30
30
  return base;
31
31
  }
32
32
 
33
- function combineEndpointConfigs(ctrl: ControllerConfig, base: EndpointConfig, ...overrides: Partial<EndpointConfig>[]): EndpointConfig {
33
+ function combineEndpointConfigs(controller: ControllerConfig, base: EndpointConfig, ...overrides: Partial<EndpointConfig>[]): EndpointConfig {
34
34
  for (const override of overrides) {
35
35
  combineCommon(base, override);
36
36
  Object.assign(
@@ -40,7 +40,7 @@ function combineEndpointConfigs(ctrl: ControllerConfig, base: EndpointConfig, ..
40
40
  httpMethod: override.httpMethod ?? base.httpMethod,
41
41
  allowsBody: override.allowsBody ?? base.allowsBody,
42
42
  path: override.path || base.path,
43
- parameters: (override.parameters ?? base.parameters).map(x => ({ ...x })),
43
+ parameters: (override.parameters ?? base.parameters).map(endpoint => ({ ...endpoint })),
44
44
  responseFinalizer: override.responseFinalizer ?? base.responseFinalizer,
45
45
  }
46
46
  );
@@ -55,17 +55,17 @@ function combineEndpointConfigs(ctrl: ControllerConfig, base: EndpointConfig, ..
55
55
  /**
56
56
  * Compute the location of a parameter within an endpoint
57
57
  */
58
- function computeParameterLocation(ep: EndpointConfig, schema: SchemaParameterConfig): EndpointParamLocation {
59
- const name = schema?.name;
60
- if (!SchemaRegistryIndex.has(schema.type)) {
61
- if ((schema.type === String || schema.type === Number) && name && ep.path.includes(`:${name.toString()}`)) {
58
+ function computeParameterLocation(endpoint: EndpointConfig, param: SchemaParameterConfig): EndpointParamLocation {
59
+ const name = param?.name;
60
+ if (!SchemaRegistryIndex.has(param.type)) {
61
+ if ((param.type === String || param.type === Number) && name && endpoint.path.includes(`:${name}`)) {
62
62
  return 'path';
63
- } else if (schema.type === Blob || schema.type === File || schema.type === ArrayBuffer || schema.type === Uint8Array) {
63
+ } else if (param.type === Blob || param.type === File || param.type === ArrayBuffer || param.type === Uint8Array) {
64
64
  return 'body';
65
65
  }
66
66
  return 'query';
67
67
  } else {
68
- return ep.allowsBody ? 'body' : 'query';
68
+ return endpoint.allowsBody ? 'body' : 'query';
69
69
  }
70
70
  }
71
71
 
@@ -74,7 +74,7 @@ function computeParameterLocation(ep: EndpointConfig, schema: SchemaParameterCon
74
74
  */
75
75
  export class ControllerRegistryAdapter implements RegistryAdapter<ControllerConfig> {
76
76
  #config: ControllerConfig;
77
- #endpoints: Map<string | symbol, EndpointConfig> = new Map();
77
+ #endpoints: Map<string, EndpointConfig> = new Map();
78
78
  #cls: Class;
79
79
  #finalizeHandlers: Function[] = [];
80
80
 
@@ -97,7 +97,7 @@ export class ControllerRegistryAdapter implements RegistryAdapter<ControllerConf
97
97
  return this.#config;
98
98
  }
99
99
 
100
- registerEndpoint(method: string | symbol, ...data: Partial<EndpointConfig>[]): EndpointConfig {
100
+ registerEndpoint(method: string, ...data: Partial<EndpointConfig>[]): EndpointConfig {
101
101
  this.register();
102
102
 
103
103
  if (!this.#endpoints.has(method)) {
@@ -108,8 +108,8 @@ export class ControllerRegistryAdapter implements RegistryAdapter<ControllerConf
108
108
  allowsBody: false,
109
109
  class: this.#cls,
110
110
  filters: [],
111
- methodName: method.toString(),
112
- id: `${this.#cls.name}#${method.toString()}`,
111
+ methodName: method,
112
+ id: `${this.#cls.name}#${method}`,
113
113
  parameters: [],
114
114
  interceptorConfigs: [],
115
115
  responseHeaders: {},
@@ -124,23 +124,23 @@ export class ControllerRegistryAdapter implements RegistryAdapter<ControllerConf
124
124
  return this.#endpoints.get(method)!;
125
125
  }
126
126
 
127
- registerEndpointParameter(method: string | symbol, index: number, ...config: Partial<EndpointParameterConfig>[]): EndpointParameterConfig {
128
- const ep = this.registerEndpoint(method);
129
- ep.parameters[index] ??= { index, location: 'query' };
130
- safeAssign(ep.parameters[index], ...config);
131
- return ep.parameters[index];
127
+ registerEndpointParameter(method: string, idx: number, ...config: Partial<EndpointParameterConfig>[]): EndpointParameterConfig {
128
+ const endpoint = this.registerEndpoint(method);
129
+ endpoint.parameters[idx] ??= { index: idx, location: 'query' };
130
+ safeAssign(endpoint.parameters[idx], ...config);
131
+ return endpoint.parameters[idx];
132
132
  }
133
133
 
134
134
  finalize(): void {
135
135
  // Merge into controller
136
- for (const ep of this.#config.endpoints) {
136
+ for (const endpoint of this.#config.endpoints) {
137
137
  // Store full path from base for use in other contexts
138
- ep.fullPath = `/${this.#config.basePath}/${ep.path}`.replace(/[/]{1,4}/g, '/').replace(/(.)[/]$/, (_, a) => a);
139
- ep.finalizedResponseHeaders = new WebHeaders({ ...this.#config.responseHeaders, ...ep.responseHeaders });
140
- ep.responseContext = { ...this.#config.responseContext, ...ep.responseContext };
141
- for (const schema of SchemaRegistryIndex.getMethodConfig(this.#cls, ep.methodName).parameters) {
142
- ep.parameters[schema.index!] ??= { index: schema.index!, location: undefined! };
143
- ep.parameters[schema.index!].location ??= computeParameterLocation(ep, schema);
138
+ endpoint.fullPath = `/${this.#config.basePath}/${endpoint.path}`.replace(/[/]{1,4}/g, '/').replace(/(.)[/]$/, (_, a) => a);
139
+ endpoint.finalizedResponseHeaders = new WebHeaders({ ...this.#config.responseHeaders, ...endpoint.responseHeaders });
140
+ endpoint.responseContext = { ...this.#config.responseContext, ...endpoint.responseContext };
141
+ for (const schema of SchemaRegistryIndex.get(this.#cls).getMethod(endpoint.methodName).parameters) {
142
+ endpoint.parameters[schema.index!] ??= { index: schema.index!, location: undefined! };
143
+ endpoint.parameters[schema.index!].location ??= computeParameterLocation(endpoint, schema);
144
144
  }
145
145
  }
146
146
  for (const item of this.#finalizeHandlers) {
@@ -153,7 +153,7 @@ export class ControllerRegistryAdapter implements RegistryAdapter<ControllerConf
153
153
  return this.#config;
154
154
  }
155
155
 
156
- getEndpointConfig(method: string | symbol): EndpointConfig {
156
+ getEndpointConfig(method: string): EndpointConfig {
157
157
  const endpoint = this.#endpoints.get(method);
158
158
  if (!endpoint) {
159
159
  throw new AppError(`Endpoint not registered: ${String(method)} on ${this.#cls.name}`);
@@ -170,7 +170,7 @@ export class ControllerRegistryAdapter implements RegistryAdapter<ControllerConf
170
170
  }
171
171
 
172
172
  registerEndpointInterceptorConfig<T extends WebInterceptor>(
173
- property: string | symbol,
173
+ property: string,
174
174
  cls: Class<T>,
175
175
  config: Partial<RetainPrimitiveFields<T['config']>>,
176
176
  extra?: Partial<EndpointConfig>
@@ -37,19 +37,19 @@ export class ControllerRegistryIndex implements RegistryIndex {
37
37
  /**
38
38
  * Register a controller/endpoint with specific config for an interceptor
39
39
  * @param cls The interceptor to register data for
40
- * @param cfg The partial config override
40
+ * @param config The partial config override
41
41
  */
42
42
  static createInterceptorConfigDecorator<T extends WebInterceptor>(
43
43
  cls: Class<T>,
44
- cfg: Partial<RetainPrimitiveFields<T['config']>>,
44
+ config: Partial<RetainPrimitiveFields<T['config']>>,
45
45
  extra?: Partial<EndpointConfig & ControllerConfig>
46
46
  ): EndpointDecorator {
47
- return (instanceOrCls: Class | ClassInstance, property?: symbol | string): void => {
47
+ return (instanceOrCls: Class | ClassInstance, property?: string): void => {
48
48
  const adapter = ControllerRegistryIndex.getForRegister(getClass(instanceOrCls));
49
49
  if (isClass(property, instanceOrCls)) {
50
- adapter.registerInterceptorConfig(cls, cfg, extra);
50
+ adapter.registerInterceptorConfig(cls, config, extra);
51
51
  } else {
52
- adapter.registerEndpointInterceptorConfig(property!, cls, cfg, extra);
52
+ adapter.registerEndpointInterceptorConfig(property!, cls, config, extra);
53
53
  }
54
54
  };
55
55
  }
@@ -62,8 +62,9 @@ export class ControllerRegistryIndex implements RegistryIndex {
62
62
  const ctx = await DependencyRegistryIndex.getInstance(WebAsyncContext);
63
63
  const cls = getClass(instance);
64
64
  const map = this.getController(cls).contextParams;
65
+ const fieldMap = SchemaRegistryIndex.get(cls).getFields();
65
66
  for (const field of Object.keys(map)) {
66
- const { type } = SchemaRegistryIndex.getFieldMap(cls)[field];
67
+ const { type } = fieldMap[field];
67
68
  Object.defineProperty(instance, field, { get: ctx.getSource(type) });
68
69
  }
69
70
  }
@@ -84,8 +85,8 @@ export class ControllerRegistryIndex implements RegistryIndex {
84
85
  return this.store.get(cls).get();
85
86
  }
86
87
 
87
- getEndpoint(cls: Class, method: string | symbol): EndpointConfig {
88
- return this.getController(cls).endpoints.find(e => e.methodName === method)!;
88
+ getEndpoint(cls: Class, method: string): EndpointConfig {
89
+ return this.getController(cls).endpoints.find(endpoint => endpoint.methodName === method)!;
89
90
  }
90
91
 
91
92
  getEndpointById(id: string): EndpointConfig | undefined {
@@ -93,16 +94,16 @@ export class ControllerRegistryIndex implements RegistryIndex {
93
94
  }
94
95
 
95
96
  process(events: ChangeEvent<Class>[]): void {
96
- for (const evt of events) {
97
- if ('curr' in evt) {
98
- for (const ep of this.getController(evt.curr).endpoints) {
99
- this.#endpointsById.set(`${evt.curr.name}#${ep.methodName.toString()}`, ep);
97
+ for (const event of events) {
98
+ if ('current' in event) {
99
+ for (const endpoint of this.getController(event.current).endpoints) {
100
+ this.#endpointsById.set(`${event.current.name}#${endpoint.methodName}`, endpoint);
100
101
  }
101
102
  } else {
102
103
  // Match by name
103
- const toDelete = [...this.#endpointsById.values()].filter(x => x.class.name === evt.prev.name);
104
- for (const ep of toDelete) {
105
- this.#endpointsById.delete(ep.id);
104
+ const toDelete = [...this.#endpointsById.values()].filter(endpoint => endpoint.class.name === event.previous.name);
105
+ for (const endpoint of toDelete) {
106
+ this.#endpointsById.delete(endpoint.id);
106
107
  }
107
108
  }
108
109
  }
@@ -16,7 +16,7 @@ export type EndpointFunctionDescriptor = TypedPropertyDescriptor<EndpointFunctio
16
16
  */
17
17
  export type EndpointDecorator = (
18
18
  (<T extends Class>(target: T) => void) &
19
- (<U>(target: U, prop: string | symbol, descriptor?: EndpointFunctionDescriptor) => void)
19
+ (<U>(target: U, property: string, descriptor?: EndpointFunctionDescriptor) => void)
20
20
  );
21
21
 
22
22
  export type EndpointParamLocation = 'path' | 'query' | 'body' | 'header';
@@ -48,7 +48,7 @@ interface CoreConfig {
48
48
  /**
49
49
  * Control which interceptors are excluded
50
50
  */
51
- interceptorExclude?: (val: WebInterceptor) => boolean;
51
+ interceptorExclude?: (value: WebInterceptor) => boolean;
52
52
  /**
53
53
  * Response headers
54
54
  */
@@ -72,7 +72,7 @@ export interface EndpointParameterConfig {
72
72
  */
73
73
  location: EndpointParamLocation;
74
74
  /**
75
- * Resolves the value by executing with req/res as input
75
+ * Resolves the value by executing with request/response as input
76
76
  */
77
77
  resolve?: WebFilter;
78
78
  /**
@@ -97,7 +97,7 @@ export interface EndpointConfig extends CoreConfig {
97
97
  /**
98
98
  * Name of the endpoint (method name)
99
99
  */
100
- methodName: string | symbol;
100
+ methodName: string;
101
101
  /**
102
102
  * Instance the endpoint is for
103
103
  */
@@ -133,7 +133,7 @@ export interface EndpointConfig extends CoreConfig {
133
133
  /**
134
134
  * Response finalizer
135
135
  */
136
- responseFinalizer?: (res: WebResponse) => WebResponse;
136
+ responseFinalizer?: (response: WebResponse) => WebResponse;
137
137
  /**
138
138
  * Response headers finalized
139
139
  */
@@ -159,7 +159,7 @@ export interface ControllerConfig extends CoreConfig {
159
159
  /**
160
160
  * Context parameters to bind at create
161
161
  */
162
- contextParams: Record<string | symbol, boolean>;
162
+ contextParams: Record<string, boolean>;
163
163
  }
164
164
 
165
165
  /**