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

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
@@ -78,10 +78,10 @@ export interface WebResponseContext {
78
78
  export class WebResponse<B = unknown> extends BaseWebMessage<B, WebResponseContext> {
79
79
 
80
80
  /**
81
- * Build the redirect
82
- * @param location Location to redirect to
83
- * @param statusCode Status code
84
- */
81
+ * Build the redirect
82
+ * @param location Location to redirect to
83
+ * @param statusCode Status code
84
+ */
85
85
  static redirect(location: string, statusCode = 302): WebResponse<undefined> {
86
86
  return new WebResponse({ context: { httpStatusCode: statusCode }, headers: { Location: location } });
87
87
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/web",
3
- "version": "7.0.0-rc.3",
3
+ "version": "7.0.0-rc.5",
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.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",
28
+ "@travetto/config": "^7.0.0-rc.4",
29
+ "@travetto/context": "^7.0.0-rc.4",
30
+ "@travetto/di": "^7.0.0-rc.4",
31
+ "@travetto/registry": "^7.0.0-rc.4",
32
+ "@travetto/runtime": "^7.0.0-rc.4",
33
+ "@travetto/schema": "^7.0.0-rc.4",
34
34
  "find-my-way": "^9.3.0"
35
35
  },
36
36
  "peerDependencies": {
37
- "@travetto/cli": "^7.0.0-rc.2",
38
- "@travetto/test": "^7.0.0-rc.2",
39
- "@travetto/transformer": "^7.0.0-rc.2"
37
+ "@travetto/cli": "^7.0.0-rc.4",
38
+ "@travetto/test": "^7.0.0-rc.4",
39
+ "@travetto/transformer": "^7.0.0-rc.3"
40
40
  },
41
41
  "peerDependenciesMeta": {
42
42
  "@travetto/transformer": {
@@ -1,5 +1,5 @@
1
- import { ChangeEvent, RegistryIndex, RegistryIndexStore, Registry } from '@travetto/registry';
2
- import { Class, ClassInstance, getClass, RetainPrimitiveFields } from '@travetto/runtime';
1
+ import { RegistryIndex, RegistryIndexStore, Registry } from '@travetto/registry';
2
+ import { Class, ClassInstance, getClass, isClass, RetainPrimitiveFields } from '@travetto/runtime';
3
3
  import { DependencyRegistryIndex } from '@travetto/di';
4
4
  import { SchemaRegistryIndex } from '@travetto/schema';
5
5
 
@@ -8,8 +8,6 @@ import { ControllerConfig, EndpointConfig, EndpointDecorator } from './types';
8
8
  import { WebAsyncContext } from '../context';
9
9
  import type { WebInterceptor } from '../types/interceptor.ts';
10
10
 
11
- const isClass = (property: unknown, target: unknown): target is Class => !property;
12
-
13
11
  export class ControllerRegistryIndex implements RegistryIndex {
14
12
 
15
13
  static #instance = Registry.registerIndex(this);
@@ -46,7 +44,7 @@ export class ControllerRegistryIndex implements RegistryIndex {
46
44
  ): EndpointDecorator {
47
45
  return (instanceOrCls: Class | ClassInstance, property?: string): void => {
48
46
  const adapter = ControllerRegistryIndex.getForRegister(getClass(instanceOrCls));
49
- if (isClass(property, instanceOrCls)) {
47
+ if (isClass(instanceOrCls)) {
50
48
  adapter.registerInterceptorConfig(cls, config, extra);
51
49
  } else {
52
50
  adapter.registerEndpointInterceptorConfig(property!, cls, config, extra);
@@ -58,6 +56,8 @@ export class ControllerRegistryIndex implements RegistryIndex {
58
56
 
59
57
  store = new RegistryIndexStore(ControllerRegistryAdapter);
60
58
 
59
+ /** @private */ constructor(source: unknown) { Registry.validateConstructor(source); }
60
+
61
61
  async #bindContextParams<T>(instance: ClassInstance<T>): Promise<void> {
62
62
  const ctx = await DependencyRegistryIndex.getInstance(WebAsyncContext);
63
63
  const cls = getClass(instance);
@@ -85,27 +85,13 @@ export class ControllerRegistryIndex implements RegistryIndex {
85
85
  return this.store.get(cls).get();
86
86
  }
87
87
 
88
- getEndpoint(cls: Class, method: string): EndpointConfig {
89
- return this.getController(cls).endpoints.find(endpoint => endpoint.methodName === method)!;
90
- }
91
-
92
88
  getEndpointById(id: string): EndpointConfig | undefined {
93
89
  return this.#endpointsById.get(id.replace(':', '#'));
94
90
  }
95
91
 
96
- process(events: ChangeEvent<Class>[]): void {
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);
101
- }
102
- } else {
103
- // Match by name
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);
107
- }
108
- }
92
+ onCreate(cls: Class): void {
93
+ for (const endpoint of this.getController(cls).endpoints) {
94
+ this.#endpointsById.set(endpoint.id, endpoint);
109
95
  }
110
96
  }
111
97
  }
@@ -1,5 +1,5 @@
1
1
  import type { Any, Class, TypedFunction } from '@travetto/runtime';
2
- import type { SchemaClassConfig, SchemaParameterConfig } from '@travetto/schema';
2
+ import type { SchemaClassConfig } from '@travetto/schema';
3
3
 
4
4
  import type { WebInterceptor } from '../types/interceptor.ts';
5
5
  import type { WebChainedFilter, WebFilter } from '../types/filter.ts';
@@ -177,16 +177,10 @@ export interface ControllerVisitor<T = unknown> {
177
177
  onControllerStart?(controller: ControllerConfig): Promise<unknown> | unknown;
178
178
  onControllerEnd?(controller: ControllerConfig): Promise<unknown> | unknown;
179
179
 
180
- onEndpointStart?(endpoint: EndpointConfig, controller: ControllerConfig, methodParams: SchemaParameterConfig[]): Promise<unknown> | unknown;
181
- onEndpointEnd?(endpoint: EndpointConfig, controller: ControllerConfig, methodParams: SchemaParameterConfig[]): Promise<unknown> | unknown;
180
+ onEndpointStart?(endpoint: EndpointConfig, controller: ControllerConfig): Promise<unknown> | unknown;
181
+ onEndpointEnd?(endpoint: EndpointConfig, controller: ControllerConfig): Promise<unknown> | unknown;
182
182
 
183
183
  onSchema?(schema: SchemaClassConfig): Promise<unknown> | unknown;
184
184
 
185
- onControllerAdd?(cls: Class): Promise<unknown> | unknown;
186
- onControllerRemove?(cls: Class): Promise<unknown> | unknown;
187
-
188
- onSchemaAdd?(cls: Class): Promise<boolean> | boolean;
189
- onSchemaRemove?(cls: Class): Promise<boolean> | boolean;
190
-
191
185
  onComplete?(): T | Promise<T>;
192
186
  }
@@ -33,15 +33,14 @@ export class ControllerVisitUtil {
33
33
  continue;
34
34
  }
35
35
 
36
- const { parameters: params, returnType } = endpointSchema;
37
- await visitor.onEndpointStart?.(endpoint, controller, params);
38
- if (returnType) {
39
- await this.#onSchemaEvent(visitor, returnType.type);
36
+ await visitor.onEndpointStart?.(endpoint, controller);
37
+ if (endpointSchema.returnType) {
38
+ await this.#onSchemaEvent(visitor, endpointSchema.returnType.type);
40
39
  }
41
- for (const param of params) {
40
+ for (const param of endpointSchema.parameters) {
42
41
  await this.#onSchemaEvent(visitor, param.type);
43
42
  }
44
- await visitor.onEndpointEnd?.(endpoint, controller, params);
43
+ await visitor.onEndpointEnd?.(endpoint, controller);
45
44
  }
46
45
  await visitor.onControllerEnd?.(controller);
47
46
  }
@@ -1,7 +1,6 @@
1
1
  import { Class, toConcrete } from '@travetto/runtime';
2
2
  import { DependencyRegistryIndex, Injectable } from '@travetto/di';
3
3
  import { ControllerRegistryIndex } from '@travetto/web';
4
- import { Registry } from '@travetto/registry';
5
4
 
6
5
  import { ControllerConfig, EndpointConfig } from '../registry/types.ts';
7
6
  import type { WebRouter } from '../types/dispatch.ts';
@@ -17,7 +16,6 @@ import { EndpointUtil } from '../util/endpoint.ts';
17
16
  @Injectable()
18
17
  export abstract class BaseWebRouter implements WebRouter {
19
18
 
20
- #cleanup = new Map<string, Function>();
21
19
  #interceptors: WebInterceptor[];
22
20
 
23
21
  async #register(cls: Class): Promise<void> {
@@ -30,8 +28,7 @@ export abstract class BaseWebRouter implements WebRouter {
30
28
  endpoint.filter = EndpointUtil.createEndpointHandler(this.#interceptors, endpoint, config);
31
29
  }
32
30
 
33
- const fn = await this.register(endpoints, config);
34
- this.#cleanup.set(cls.Ⲑid, fn);
31
+ await this.register(endpoints, config);
35
32
  };
36
33
 
37
34
  /**
@@ -48,22 +45,8 @@ export abstract class BaseWebRouter implements WebRouter {
48
45
  for (const cls of ControllerRegistryIndex.getClasses()) {
49
46
  await this.#register(cls);
50
47
  }
51
-
52
- // Listen for updates
53
- Registry.onClassChange(async event => {
54
- const targetCls = 'current' in event ? event.current : event.previous;
55
- console.debug('Registry event', { type: event.type, target: targetCls.Ⲑid });
56
-
57
- if ('previous' in event) {
58
- this.#cleanup.get(event.previous.Ⲑid)?.();
59
- this.#cleanup.delete(event.previous.Ⲑid);
60
- }
61
- if ('current' in event) {
62
- await this.#register(event.current);
63
- }
64
- }, ControllerRegistryIndex);
65
48
  }
66
49
 
67
- abstract register(endpoints: EndpointConfig[], controller: ControllerConfig): Promise<() => void>;
50
+ abstract register(endpoints: EndpointConfig[], controller: ControllerConfig): Promise<void>;
68
51
  abstract dispatch(ctx: WebFilterContext): Promise<WebResponse>;
69
52
  }
@@ -26,19 +26,13 @@ export class StandardWebRouter extends BaseWebRouter {
26
26
  #cache = new Map<Function, EndpointConfig>();
27
27
  raw = router();
28
28
 
29
- async register(endpoints: EndpointConfig[]): Promise<() => void> {
29
+ async register(endpoints: EndpointConfig[]): Promise<void> {
30
30
  for (const endpoint of endpoints) {
31
31
  const fullPath = endpoint.fullPath.replace(/[*][^*]+/g, '*'); // Flatten wildcards
32
32
  const handler = (): void => { };
33
33
  this.#cache.set(handler, endpoint);
34
34
  this.raw[HTTP_METHODS[endpoint.httpMethod ?? DEFAULT_HTTP_METHOD].lower](fullPath, handler);
35
35
  }
36
-
37
- return (): void => {
38
- for (const endpoint of endpoints ?? []) {
39
- this.raw.off(endpoint.httpMethod ?? DEFAULT_HTTP_METHOD, endpoint.fullPath);
40
- }
41
- };
42
36
  }
43
37
 
44
38
  /**
@@ -19,5 +19,5 @@ export interface WebRouter extends WebDispatcher {
19
19
  /**
20
20
  * Register a controller with the prepared endpoints
21
21
  */
22
- register(endpoints: EndpointConfig[], controller: ControllerConfig): Promise<() => void>;
22
+ register(endpoints: EndpointConfig[], controller: ControllerConfig): Promise<void>;
23
23
  }
@@ -12,10 +12,10 @@ export interface WebResponseContext {
12
12
  export class WebResponse<B = unknown> extends BaseWebMessage<B, WebResponseContext> {
13
13
 
14
14
  /**
15
- * Build the redirect
16
- * @param location Location to redirect to
17
- * @param statusCode Status code
18
- */
15
+ * Build the redirect
16
+ * @param location Location to redirect to
17
+ * @param statusCode Status code
18
+ */
19
19
  static redirect(location: string, statusCode = 302): WebResponse<undefined> {
20
20
  return new WebResponse({ context: { httpStatusCode: statusCode }, headers: { Location: location } });
21
21
  }
package/src/util/body.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { TextDecoder } from 'node:util';
2
2
  import { Readable } from 'node:stream';
3
3
 
4
- import { Any, BinaryUtil, castTo, hasToJSON, Util } from '@travetto/runtime';
4
+ import { Any, BinaryUtil, castTo, hasToJSON, JSONUtil, Util } from '@travetto/runtime';
5
5
 
6
6
  import { WebBinaryBody, WebMessage } from '../types/message.ts';
7
7
  import { WebHeaders } from '../types/headers.ts';
@@ -160,7 +160,7 @@ export class WebBodyUtil {
160
160
  static parseBody(type: string, body: string): unknown {
161
161
  switch (type) {
162
162
  case 'text': return body;
163
- case 'json': return JSON.parse(body);
163
+ case 'json': return JSONUtil.parseSafe(body);
164
164
  case 'form': return Object.fromEntries(new URLSearchParams(body));
165
165
  }
166
166
  }
@@ -1,7 +1,6 @@
1
- import { asConstructable, castKey, castTo, Class, Runtime, TypedObject } from '@travetto/runtime';
1
+ import { asConstructable, castKey, castTo, Class, TypedObject } from '@travetto/runtime';
2
2
  import { BindUtil, SchemaParameterConfig, SchemaRegistryIndex, SchemaValidator, ValidationResultError } from '@travetto/schema';
3
3
  import { DependencyRegistryIndex } from '@travetto/di';
4
- import { RetargettingProxy } from '@travetto/registry';
5
4
 
6
5
  import { WebChainedFilter, WebChainedContext, WebFilter } from '../types/filter.ts';
7
6
  import { WebResponse } from '../types/response.ts';
@@ -240,10 +239,6 @@ export class EndpointUtil {
240
239
 
241
240
  config.instance = await DependencyRegistryIndex.getInstance(config.class);
242
241
 
243
- if (Runtime.dynamic) {
244
- config.instance = RetargettingProxy.unwrap(config.instance);
245
- }
246
-
247
242
  // Filter out conditional endpoints
248
243
  const endpoints = (await Promise.all(
249
244
  config.endpoints.map(endpoint => Promise.resolve(endpoint.conditional?.() ?? true).then(value => value ? endpoint : undefined))
@@ -1,7 +1,7 @@
1
1
  import { buffer } from 'node:stream/consumers';
2
2
  import { Readable } from 'node:stream';
3
3
 
4
- import { AppError, BinaryUtil, castTo } from '@travetto/runtime';
4
+ import { AppError, BinaryUtil, castTo, JSONUtil } from '@travetto/runtime';
5
5
  import { BindUtil } from '@travetto/schema';
6
6
 
7
7
  import { WebResponse } from '../../src/types/response.ts';
@@ -51,7 +51,7 @@ export class WebTestDispatchUtil {
51
51
 
52
52
  if (text) {
53
53
  switch (response.headers.get('Content-Type')) {
54
- case 'application/json': result = JSON.parse(castTo(text)); break;
54
+ case 'application/json': result = JSONUtil.parseSafe(castTo(text)); break;
55
55
  case 'text/plain': result = text; break;
56
56
  }
57
57
  }
@@ -1,19 +1,13 @@
1
1
  import assert from 'node:assert';
2
2
 
3
- import { Test, Suite, BeforeAll } from '@travetto/test';
4
- import { Registry } from '@travetto/registry';
3
+ import { Test, Suite } from '@travetto/test';
5
4
 
6
5
  import { BaseWebSuite } from './base.ts';
7
- import { TestController } from './controller.ts';
6
+ import './controller.ts'; // Ensure imported
8
7
 
9
8
  @Suite()
10
9
  export abstract class StandardWebServerSuite extends BaseWebSuite {
11
10
 
12
- @BeforeAll()
13
- async init() {
14
- Registry.process([{ type: 'added', curr: TestController }]);
15
- }
16
-
17
11
  @Test()
18
12
  async getJSON() {
19
13
  const response = await this.request({ context: { httpMethod: 'GET', path: '/test/json' } });