@travetto/registry 6.0.0-rc.0 → 6.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
@@ -69,7 +69,7 @@ export class SampleRegistry extends MetadataRegistry<Group, Child> {
69
69
  }
70
70
  ```
71
71
 
72
- The registry is a [MetadataRegistry](https://github.com/travetto/travetto/tree/main/module/registry/src/service/metadata.ts#L13) that similar to the [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding.")'s Schema registry and [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support.")'s Dependency registry.
72
+ The registry is a [MetadataRegistry](https://github.com/travetto/travetto/tree/main/module/registry/src/service/metadata.ts#L14) that similar to the [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding.")'s Schema registry and [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support.")'s Dependency registry.
73
73
 
74
74
  ### Live Flow
75
75
  At runtime, the registry is designed to listen for changes and to propagate the changes as necessary. In many cases the same file is handled by multiple registries.
@@ -84,16 +84,17 @@ As mentioned in [Manifest](https://github.com/travetto/travetto/tree/main/module
84
84
 
85
85
  #handleFileChanges(importFile: string, classes: Class[] = []): number {
86
86
  const next = new Map<string, Class>(classes.map(cls => [cls.Ⲑid, cls] as const));
87
+ const sourceFile = RuntimeIndex.getSourceFile(importFile);
87
88
 
88
89
  let prev = new Map<string, Class>();
89
- if (this.#classes.has(importFile)) {
90
- prev = new Map(this.#classes.get(importFile)!.entries());
90
+ if (this.#classes.has(sourceFile)) {
91
+ prev = new Map(this.#classes.get(sourceFile)!.entries());
91
92
  }
92
93
 
93
94
  const keys = new Set([...Array.from(prev.keys()), ...Array.from(next.keys())]);
94
95
 
95
- if (!this.#classes.has(importFile)) {
96
- this.#classes.set(importFile, new Map());
96
+ if (!this.#classes.has(sourceFile)) {
97
+ this.#classes.set(sourceFile, new Map());
97
98
  }
98
99
 
99
100
  let changes = 0;
@@ -103,9 +104,9 @@ As mentioned in [Manifest](https://github.com/travetto/travetto/tree/main/module
103
104
  if (!next.has(k)) {
104
105
  changes += 1;
105
106
  this.emit({ type: 'removing', prev: prev.get(k)! });
106
- this.#classes.get(importFile)!.delete(k);
107
+ this.#classes.get(sourceFile)!.delete(k);
107
108
  } else {
108
- this.#classes.get(importFile)!.set(k, next.get(k)!);
109
+ this.#classes.get(sourceFile)!.set(k, next.get(k)!);
109
110
  if (!prev.has(k)) {
110
111
  changes += 1;
111
112
  this.emit({ type: 'added', curr: next.get(k)! });
package/__index__.ts CHANGED
@@ -1,8 +1,8 @@
1
- export * from './src/registry';
2
- export * from './src/service/metadata';
3
- export * from './src/service/root';
4
- export * from './src/proxy';
5
- export * from './src/registry';
6
- export * from './src/source/class-source';
7
- export * from './src/source/method-source';
8
- export * from './src/types';
1
+ export * from './src/registry.ts';
2
+ export * from './src/service/metadata.ts';
3
+ export * from './src/service/root.ts';
4
+ export * from './src/proxy.ts';
5
+ export * from './src/registry.ts';
6
+ export * from './src/source/class-source.ts';
7
+ export * from './src/source/method-source.ts';
8
+ export * from './src/types.ts';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/registry",
3
- "version": "6.0.0-rc.0",
3
+ "version": "6.0.0-rc.2",
4
4
  "description": "Patterns and utilities for handling registration of metadata and functionality for run-time use",
5
5
  "keywords": [
6
6
  "ast-transformations",
@@ -27,11 +27,11 @@
27
27
  "directory": "module/registry"
28
28
  },
29
29
  "dependencies": {
30
- "@travetto/runtime": "^6.0.0-rc.0"
30
+ "@travetto/runtime": "^6.0.0-rc.2"
31
31
  },
32
32
  "peerDependencies": {
33
- "@travetto/cli": "^6.0.0-rc.0",
34
- "@travetto/transformer": "^6.0.0-rc.0"
33
+ "@travetto/cli": "^6.0.0-rc.2",
34
+ "@travetto/transformer": "^6.0.0-rc.3"
35
35
  },
36
36
  "peerDependenciesMeta": {
37
37
  "@travetto/transformer": {
@@ -3,7 +3,7 @@ import { Module } from 'node:module';
3
3
  import { path } from '@travetto/manifest';
4
4
  import { Runtime, RuntimeIndex } from '@travetto/runtime';
5
5
 
6
- import { RetargettingProxy } from '../proxy';
6
+ import { RetargettingProxy } from '../proxy.ts';
7
7
 
8
8
  declare module 'module' {
9
9
  // eslint-disable-next-line @typescript-eslint/naming-convention
@@ -1,78 +1,46 @@
1
1
  import { ManifestModuleUtil } from '@travetto/manifest';
2
2
  import { watchCompiler, WatchEvent, Runtime, RuntimeIndex } from '@travetto/runtime';
3
3
 
4
- interface ModuleLoader {
5
- init?(): Promise<void>;
6
- load(file: string): Promise<void>;
7
- unload(file: string): Promise<void>;
8
- }
9
-
10
- type Handler = (ev: WatchEvent) => unknown;
11
-
12
4
  const VALID_FILE_TYPES = new Set(['js', 'ts']);
13
5
 
14
- /**
15
- * Listens to file changes, and provides a unified interface for watching file changes, reloading files as needed
16
- */
17
- class $DynamicFileLoader {
18
- #handlers: Handler[] = [];
19
- #loader: ModuleLoader;
20
- #initialized = false;
21
-
22
- async dispatch(ev: WatchEvent): Promise<void> {
23
- if (ev.action === 'update' || ev.action === 'delete') {
24
- await this.#loader.unload(ev.output);
25
- }
26
- if (ev.action === 'create' || ev.action === 'delete') {
27
- RuntimeIndex.reinitForModule(Runtime.main.name);
28
- }
29
- if (ev.action === 'create' || ev.action === 'update') {
30
- await this.#loader.load(ev.output);
31
- }
32
-
33
- for (const handler of this.#handlers) {
34
- await handler(ev);
35
- }
36
- }
37
-
38
- onLoadEvent(handler: Handler): void {
39
- this.#handlers.push(handler);
6
+ const handle = (err: Error): void => {
7
+ if (err && (err.message ?? '').includes('Cannot find module')) { // Handle module reloading
8
+ console.error('Cannot find module', { error: err });
9
+ } else {
10
+ throw err;
40
11
  }
12
+ };
41
13
 
42
- async init(): Promise<void> {
43
- if (this.#initialized) {
44
- return;
45
- }
46
-
47
- this.#initialized = true;
14
+ /**
15
+ * Listens to file changes, and handles loading/unloading as needed (when supported)
16
+ */
17
+ export class DynamicFileLoader {
48
18
 
19
+ static async * listen(): AsyncIterable<WatchEvent> {
49
20
  // TODO: ESM Support?
50
- const { DynamicCommonjsLoader } = await import('./commonjs-loader');
51
- this.#loader = new DynamicCommonjsLoader();
52
-
53
- await this.#loader.init?.();
54
-
55
- const handle = (err: Error): void => {
56
- if (err && (err.message ?? '').includes('Cannot find module')) { // Handle module reloading
57
- console.error('Cannot find module', { error: err });
58
- } else {
59
- throw err;
60
- }
61
- };
21
+ const { DynamicCommonjsLoader } = await import('./commonjs-loader.ts');
22
+ const loader = new DynamicCommonjsLoader();
23
+ await loader.init?.();
62
24
 
63
25
  process
64
26
  .on('unhandledRejection', handle)
65
27
  .on('uncaughtException', handle);
66
28
 
67
29
  // Fire off, and let it run in the bg. Restart on exit
68
- (async (): Promise<void> => {
69
- for await (const ev of watchCompiler({ restartOnExit: true })) {
70
- if (ev.file && RuntimeIndex.hasModule(ev.module) && VALID_FILE_TYPES.has(ManifestModuleUtil.getFileType(ev.file))) {
71
- await this.dispatch(ev);
30
+ for await (const ev of watchCompiler({ restartOnExit: true })) {
31
+ if (ev.file && RuntimeIndex.hasModule(ev.module) && VALID_FILE_TYPES.has(ManifestModuleUtil.getFileType(ev.file))) {
32
+ if (ev.action === 'update' || ev.action === 'delete') {
33
+ await loader.unload(ev.output);
34
+ }
35
+ if (ev.action === 'create' || ev.action === 'delete') {
36
+ RuntimeIndex.reinitForModule(Runtime.main.name);
37
+ }
38
+ if (ev.action === 'create' || ev.action === 'update') {
39
+ await loader.load(ev.output);
72
40
  }
41
+
42
+ yield ev;
73
43
  }
74
- })();
44
+ }
75
45
  }
76
- }
77
-
78
- export const DynamicFileLoader = new $DynamicFileLoader();
46
+ }
package/src/proxy.ts CHANGED
@@ -1,6 +1,7 @@
1
+ /* eslint @typescript-eslint/no-unused-vars: ["error", { "args": "none", "varsIgnorePattern": "^(_.*|[A-Z])$" } ] */
1
2
  import { Any, castKey, castTo, classConstruct } from '@travetto/runtime';
2
3
 
3
- const ProxyTargetSymbol = Symbol.for('@travetto/runtime:proxy-target');
4
+ const ProxyTargetSymbol = Symbol();
4
5
 
5
6
  const AsyncGeneratorFunction = Object.getPrototypeOf(async function* () { });
6
7
  const GeneratorFunction = Object.getPrototypeOf(function* () { });
@@ -54,12 +55,12 @@ export class RetargettingHandler<T> implements ProxyHandler<Any> {
54
55
  if (prop === ProxyTargetSymbol) {
55
56
  return this.target;
56
57
  }
57
- let ret = this.target[castKey<T>(prop)];
58
- if (isFunction(ret) && !/^class\s/.test(Function.prototype.toString.call(ret))) {
58
+ let result = this.target[castKey<T>(prop)];
59
+ if (isFunction(result) && !/^class\s/.test(Function.prototype.toString.call(result))) {
59
60
  // Bind class members to class instance instead of proxy propagating
60
- ret = ret.bind(this.target);
61
+ result = result.bind(this.target);
61
62
  }
62
- return ret;
63
+ return result;
63
64
  }
64
65
 
65
66
  has(target: T, prop: PropertyKey): boolean {
@@ -123,4 +124,4 @@ export class RetargettingProxy<T> {
123
124
  get<V extends T>(): V {
124
125
  return castTo(this.#instance);
125
126
  }
126
- }
127
+ }
package/src/registry.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { EventEmitter } from 'node:events';
2
2
  import { Class, Env } from '@travetto/runtime';
3
- import { ChangeSource, ChangeEvent, ChangeHandler } from './types';
3
+ import { ChangeSource, ChangeEvent, ChangeHandler } from './types.ts';
4
4
 
5
5
  /**
6
6
  * Base registry class, designed to listen to changes over time
@@ -1,7 +1,8 @@
1
+ /* eslint @typescript-eslint/no-unused-vars: ["error", { "args": "none"} ] */
1
2
  import { Class } from '@travetto/runtime';
2
3
 
3
- import { Registry } from '../registry';
4
- import { ChangeEvent } from '../types';
4
+ import { Registry } from '../registry.ts';
5
+ import { ChangeEvent } from '../types.ts';
5
6
 
6
7
  function id(cls: string | Class): string {
7
8
  return cls && typeof cls !== 'string' ? cls.Ⲑid : cls;
@@ -1,8 +1,8 @@
1
1
  import { Class } from '@travetto/runtime';
2
2
 
3
- import { Registry } from '../registry';
4
- import { ClassSource } from '../source/class-source';
5
- import { ChangeEvent } from '../types';
3
+ import { Registry } from '../registry.ts';
4
+ import { ClassSource } from '../source/class-source.ts';
5
+ import { ChangeEvent } from '../types.ts';
6
6
 
7
7
  /**
8
8
  * The root registry that controls all registries
@@ -2,8 +2,8 @@ import { EventEmitter } from 'node:events';
2
2
 
3
3
  import { Class, Env, Runtime, RuntimeIndex, describeFunction, flushPendingFunctions } from '@travetto/runtime';
4
4
 
5
- import { DynamicFileLoader } from '../internal/file-loader';
6
- import { ChangeSource, ChangeEvent, ChangeHandler } from '../types';
5
+ import { DynamicFileLoader } from '../internal/file-loader.ts';
6
+ import { ChangeSource, ChangeEvent, ChangeHandler } from '../types.ts';
7
7
 
8
8
  function isClass(cls: Function): cls is Class {
9
9
  return !!describeFunction(cls).class;
@@ -18,6 +18,7 @@ export class ClassSource implements ChangeSource<Class> {
18
18
 
19
19
  #classes = new Map<string, Map<string, Class>>();
20
20
  #emitter = new EventEmitter();
21
+ #listening: Promise<void> | undefined;
21
22
 
22
23
  /**
23
24
  * Are we in a mode that should have enhanced debug info
@@ -29,7 +30,7 @@ export class ClassSource implements ChangeSource<Class> {
29
30
  */
30
31
  #flush(): void {
31
32
  for (const cls of flushPendingFunctions().filter(isClass)) {
32
- const src = Runtime.getImport(cls);
33
+ const src = Runtime.getSourceFile(cls);
33
34
  if (!this.#classes.has(src)) {
34
35
  this.#classes.set(src, new Map());
35
36
  }
@@ -38,21 +39,32 @@ export class ClassSource implements ChangeSource<Class> {
38
39
  }
39
40
  }
40
41
 
42
+ #removeFile(file: string): void {
43
+ const data = this.#classes.get(file);
44
+ if (data) {
45
+ this.#classes.delete(file);
46
+ for (const cls of data) {
47
+ this.emit({ type: 'removing', prev: cls[1] });
48
+ }
49
+ }
50
+ }
51
+
41
52
  /**
42
53
  * Process changes for a single file, looking for add/remove/update of classes
43
54
  */
44
55
  #handleFileChanges(importFile: string, classes: Class[] = []): number {
45
56
  const next = new Map<string, Class>(classes.map(cls => [cls.Ⲑid, cls] as const));
57
+ const sourceFile = RuntimeIndex.getSourceFile(importFile);
46
58
 
47
59
  let prev = new Map<string, Class>();
48
- if (this.#classes.has(importFile)) {
49
- prev = new Map(this.#classes.get(importFile)!.entries());
60
+ if (this.#classes.has(sourceFile)) {
61
+ prev = new Map(this.#classes.get(sourceFile)!.entries());
50
62
  }
51
63
 
52
64
  const keys = new Set([...Array.from(prev.keys()), ...Array.from(next.keys())]);
53
65
 
54
- if (!this.#classes.has(importFile)) {
55
- this.#classes.set(importFile, new Map());
66
+ if (!this.#classes.has(sourceFile)) {
67
+ this.#classes.set(sourceFile, new Map());
56
68
  }
57
69
 
58
70
  let changes = 0;
@@ -62,9 +74,9 @@ export class ClassSource implements ChangeSource<Class> {
62
74
  if (!next.has(k)) {
63
75
  changes += 1;
64
76
  this.emit({ type: 'removing', prev: prev.get(k)! });
65
- this.#classes.get(importFile)!.delete(k);
77
+ this.#classes.get(sourceFile)!.delete(k);
66
78
  } else {
67
- this.#classes.get(importFile)!.set(k, next.get(k)!);
79
+ this.#classes.get(sourceFile)!.set(k, next.get(k)!);
68
80
  if (!prev.has(k)) {
69
81
  changes += 1;
70
82
  this.emit({ type: 'added', curr: next.get(k)! });
@@ -115,15 +127,20 @@ export class ClassSource implements ChangeSource<Class> {
115
127
  * Initialize
116
128
  */
117
129
  async init(): Promise<void> {
118
- if (Runtime.dynamic) {
119
- DynamicFileLoader.onLoadEvent(ev => {
120
- this.#handleChanges(flushPendingFunctions().filter(isClass));
130
+ if (Runtime.dynamic && !this.#listening) {
131
+ this.#listening = (async (): Promise<void> => {
132
+ for await (const ev of await DynamicFileLoader.listen()) {
133
+ if (ev.action === 'delete') {
134
+ this.#removeFile(ev.file); // File no longer exists
135
+ } else {
136
+ this.#handleChanges(flushPendingFunctions().filter(isClass));
137
+ }
121
138
 
122
- if (ev.action === 'create') {
123
- this.#flush();
139
+ if (ev.action === 'create') {
140
+ this.#flush();
141
+ }
124
142
  }
125
- });
126
- await DynamicFileLoader.init();
143
+ })();
127
144
  }
128
145
 
129
146
  // Ensure everything is loaded
@@ -2,7 +2,7 @@ import { EventEmitter } from 'node:events';
2
2
 
3
3
  import { Class, describeFunction } from '@travetto/runtime';
4
4
 
5
- import { ChangeSource, ChangeEvent, ChangeHandler } from '../types';
5
+ import { ChangeSource, ChangeEvent, ChangeHandler } from '../types.ts';
6
6
 
7
7
  /**
8
8
  * Change source specific to individual methods of classes. Useful