@travetto/web 7.0.0-rc.0 → 7.0.0-rc.2

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
@@ -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
@@ -735,7 +735,7 @@ 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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/web",
3
- "version": "7.0.0-rc.0",
3
+ "version": "7.0.0-rc.2",
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.1",
29
+ "@travetto/context": "^7.0.0-rc.1",
30
+ "@travetto/di": "^7.0.0-rc.1",
31
+ "@travetto/registry": "^7.0.0-rc.1",
32
+ "@travetto/runtime": "^7.0.0-rc.1",
33
+ "@travetto/schema": "^7.0.0-rc.1",
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.1",
38
+ "@travetto/test": "^7.0.0-rc.1",
39
+ "@travetto/transformer": "^7.0.0-rc.1"
40
40
  },
41
41
  "peerDependenciesMeta": {
42
42
  "@travetto/transformer": {
@@ -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 {
16
+ export function Param(location: EndpointParamLocation, aliasOrConfig: string | Partial<EndpointParameterConfig>): ParamDecorator {
17
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 });
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
@@ -108,7 +108,6 @@ export class ControllerRegistryAdapter implements RegistryAdapter<ControllerConf
108
108
  allowsBody: false,
109
109
  class: this.#cls,
110
110
  filters: [],
111
- endpoint: this.#cls.prototype[method],
112
111
  methodName: method.toString(),
113
112
  id: `${this.#cls.name}#${method.toString()}`,
114
113
  parameters: [],
@@ -125,11 +124,11 @@ export class ControllerRegistryAdapter implements RegistryAdapter<ControllerConf
125
124
  return this.#endpoints.get(method)!;
126
125
  }
127
126
 
128
- registerEndpointParameter(method: string | symbol, index: number, ...config: Partial<EndpointParameterConfig>[]): EndpointParameterConfig {
127
+ registerEndpointParameter(method: string | symbol, idx: number, ...config: Partial<EndpointParameterConfig>[]): EndpointParameterConfig {
129
128
  const ep = this.registerEndpoint(method);
130
- ep.parameters[index] ??= { index, location: 'query' };
131
- safeAssign(ep.parameters[index], ...config);
132
- return ep.parameters[index];
129
+ ep.parameters[idx] ??= { index: idx, location: 'query' };
130
+ safeAssign(ep.parameters[idx], ...config);
131
+ return ep.parameters[idx];
133
132
  }
134
133
 
135
134
  finalize(): void {
@@ -139,7 +138,7 @@ export class ControllerRegistryAdapter implements RegistryAdapter<ControllerConf
139
138
  ep.fullPath = `/${this.#config.basePath}/${ep.path}`.replace(/[/]{1,4}/g, '/').replace(/(.)[/]$/, (_, a) => a);
140
139
  ep.finalizedResponseHeaders = new WebHeaders({ ...this.#config.responseHeaders, ...ep.responseHeaders });
141
140
  ep.responseContext = { ...this.#config.responseContext, ...ep.responseContext };
142
- for (const schema of SchemaRegistryIndex.getMethodConfig(this.#cls, ep.methodName).parameters) {
141
+ for (const schema of SchemaRegistryIndex.get(this.#cls).getMethod(ep.methodName).parameters) {
143
142
  ep.parameters[schema.index!] ??= { index: schema.index!, location: undefined! };
144
143
  ep.parameters[schema.index!].location ??= computeParameterLocation(ep, schema);
145
144
  }
@@ -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
  }
@@ -118,10 +118,6 @@ export interface EndpointConfig extends CoreConfig {
118
118
  * The path of the endpoint
119
119
  */
120
120
  path: string;
121
- /**
122
- * The function the endpoint will call
123
- */
124
- endpoint: EndpointFunction;
125
121
  /**
126
122
  * The compiled and finalized handler
127
123
  */
@@ -28,12 +28,12 @@ export class ControllerVisitUtil {
28
28
 
29
29
  await visitor.onControllerStart?.(controller);
30
30
  for (const endpoint of controller.endpoints) {
31
- const endpointSchema = SchemaRegistryIndex.getMethodConfig(cls, endpoint.methodName);
31
+ const endpointSchema = SchemaRegistryIndex.get(cls).getMethod(endpoint.methodName);
32
32
  if (endpointSchema.private === true && options.skipPrivate) {
33
33
  continue;
34
34
  }
35
35
 
36
- const { parameters: params, returnType } = SchemaRegistryIndex.getMethodConfig(cls, endpoint.methodName);
36
+ const { parameters: params, returnType } = endpointSchema;
37
37
  await visitor.onEndpointStart?.(endpoint, controller, params);
38
38
  if (returnType) {
39
39
  await this.#onSchemaEvent(visitor, returnType.type);
@@ -1,4 +1,4 @@
1
- import { asConstructable, castTo, Class, Runtime, TypedObject } from '@travetto/runtime';
1
+ import { asConstructable, castKey, castTo, Class, Runtime, TypedObject } from '@travetto/runtime';
2
2
  import { BindUtil, SchemaParameterConfig, SchemaRegistryIndex, SchemaValidator, ValidationResultError } from '@travetto/schema';
3
3
  import { DependencyRegistryIndex } from '@travetto/di';
4
4
  import { RetargettingProxy } from '@travetto/registry';
@@ -8,7 +8,7 @@ import { WebResponse } from '../types/response.ts';
8
8
  import { WebInterceptor } from '../types/interceptor.ts';
9
9
  import { WebRequest } from '../types/request.ts';
10
10
  import { WEB_INTERCEPTOR_CATEGORIES } from '../types/core.ts';
11
- import { EndpointConfig, ControllerConfig, EndpointParameterConfig } from '../registry/types.ts';
11
+ import { EndpointConfig, ControllerConfig, EndpointParameterConfig, EndpointFunction } from '../registry/types.ts';
12
12
  import { ControllerRegistryIndex } from '../registry/registry-index.ts';
13
13
  import { WebCommonUtil } from './common.ts';
14
14
 
@@ -122,14 +122,9 @@ export class EndpointUtil {
122
122
  }
123
123
  }
124
124
 
125
- let res = this.extractParameterValue(request, param, input.name!.toString(), input.array);
126
- if (!res && input.aliases) {
127
- for (const name of input.aliases) {
128
- res = this.extractParameterValue(request, param, name, input.array);
129
- if (res !== undefined) {
130
- break;
131
- }
132
- }
125
+ let res = this.extractParameterValue(request, param, input.name!.toString(), input.array) ?? undefined;
126
+ for (let i = 0; res === undefined && input.aliases && i < input.aliases.length; i += 1) {
127
+ res = this.extractParameterValue(request, param, input.aliases[i], input.array) ?? undefined;
133
128
  }
134
129
  return res;
135
130
  }
@@ -143,7 +138,7 @@ export class EndpointUtil {
143
138
  static async extractParameters(endpoint: EndpointConfig, request: WebRequest): Promise<unknown[]> {
144
139
  const cls = endpoint.class;
145
140
  const vals = WebCommonUtil.getRequestParams(request);
146
- const { parameters } = SchemaRegistryIndex.getMethodConfig(cls, endpoint.methodName);
141
+ const { parameters } = SchemaRegistryIndex.get(cls).getMethod(endpoint.methodName);
147
142
  const combined = parameters.map((cfg) =>
148
143
  ({ schema: cfg, param: endpoint.parameters[cfg.index], value: vals?.[cfg.index] }));
149
144
 
@@ -177,7 +172,7 @@ export class EndpointUtil {
177
172
  static async invokeEndpoint(endpoint: EndpointConfig, { request }: WebChainedContext): Promise<WebResponse> {
178
173
  try {
179
174
  const params = await this.extractParameters(endpoint, request);
180
- const body = await endpoint.endpoint.apply(endpoint.instance, params);
175
+ const body = await castTo<EndpointFunction>(endpoint.instance![castKey(endpoint.methodName)]).apply(endpoint.instance, params);
181
176
  const headers = endpoint.finalizedResponseHeaders;
182
177
  let response: WebResponse;
183
178
  if (body instanceof WebResponse) {
@@ -110,7 +110,7 @@ function getEndpoint(path: string, method: HttpMethod) {
110
110
 
111
111
  function getEndpointResponse(path: string, method: HttpMethod) {
112
112
  const ep = getEndpoint(path, method);
113
- const resp = SchemaRegistryIndex.getMethodConfig(SchemaAPI, ep.methodName);
113
+ const resp = SchemaRegistryIndex.get(SchemaAPI).getMethod(ep.methodName);
114
114
  return resp?.returnType?.type;
115
115
  }
116
116