@travetto/registry 6.0.0-rc.1 → 6.0.0
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 +9 -7
- package/__index__.ts +8 -8
- package/package.json +4 -4
- package/src/internal/commonjs-loader.ts +1 -1
- package/src/internal/file-loader.ts +28 -60
- package/src/proxy.ts +7 -6
- package/src/registry.ts +1 -1
- package/src/service/metadata.ts +3 -2
- package/src/service/root.ts +3 -3
- package/src/source/class-source.ts +33 -16
- package/src/source/method-source.ts +1 -1
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ The primary flow occurs on initialization of the application. At that point, the
|
|
|
23
23
|
1. Initialize [RootRegistry](https://github.com/travetto/travetto/tree/main/module/registry/src/service/root.ts#L10) and will automatically register/load all relevant files
|
|
24
24
|
1. As files are imported, decorators within the files will record various metadata relevant to the respective registries
|
|
25
25
|
1. When all files are processed, the [RootRegistry](https://github.com/travetto/travetto/tree/main/module/registry/src/service/root.ts#L10) is finished, and it will signal to anything waiting on registered data that its free to use it.
|
|
26
|
+
|
|
26
27
|
This flow ensures all files are loaded and processed before application starts. A sample registry could like:
|
|
27
28
|
|
|
28
29
|
**Code: Sample Registry**
|
|
@@ -69,7 +70,7 @@ export class SampleRegistry extends MetadataRegistry<Group, Child> {
|
|
|
69
70
|
}
|
|
70
71
|
```
|
|
71
72
|
|
|
72
|
-
The registry is a [MetadataRegistry](https://github.com/travetto/travetto/tree/main/module/registry/src/service/metadata.ts#
|
|
73
|
+
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
74
|
|
|
74
75
|
### Live Flow
|
|
75
76
|
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 +85,17 @@ As mentioned in [Manifest](https://github.com/travetto/travetto/tree/main/module
|
|
|
84
85
|
|
|
85
86
|
#handleFileChanges(importFile: string, classes: Class[] = []): number {
|
|
86
87
|
const next = new Map<string, Class>(classes.map(cls => [cls.Ⲑid, cls] as const));
|
|
88
|
+
const sourceFile = RuntimeIndex.getSourceFile(importFile);
|
|
87
89
|
|
|
88
90
|
let prev = new Map<string, Class>();
|
|
89
|
-
if (this.#classes.has(
|
|
90
|
-
prev = new Map(this.#classes.get(
|
|
91
|
+
if (this.#classes.has(sourceFile)) {
|
|
92
|
+
prev = new Map(this.#classes.get(sourceFile)!.entries());
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
const keys = new Set([...Array.from(prev.keys()), ...Array.from(next.keys())]);
|
|
94
96
|
|
|
95
|
-
if (!this.#classes.has(
|
|
96
|
-
this.#classes.set(
|
|
97
|
+
if (!this.#classes.has(sourceFile)) {
|
|
98
|
+
this.#classes.set(sourceFile, new Map());
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
let changes = 0;
|
|
@@ -103,9 +105,9 @@ As mentioned in [Manifest](https://github.com/travetto/travetto/tree/main/module
|
|
|
103
105
|
if (!next.has(k)) {
|
|
104
106
|
changes += 1;
|
|
105
107
|
this.emit({ type: 'removing', prev: prev.get(k)! });
|
|
106
|
-
this.#classes.get(
|
|
108
|
+
this.#classes.get(sourceFile)!.delete(k);
|
|
107
109
|
} else {
|
|
108
|
-
this.#classes.get(
|
|
110
|
+
this.#classes.get(sourceFile)!.set(k, next.get(k)!);
|
|
109
111
|
if (!prev.has(k)) {
|
|
110
112
|
changes += 1;
|
|
111
113
|
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
|
|
3
|
+
"version": "6.0.0",
|
|
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
|
|
30
|
+
"@travetto/runtime": "^6.0.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@travetto/cli": "^6.0.0
|
|
34
|
-
"@travetto/transformer": "^6.0.0
|
|
33
|
+
"@travetto/cli": "^6.0.0",
|
|
34
|
+
"@travetto/transformer": "^6.0.0"
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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
|
-
(
|
|
69
|
-
|
|
70
|
-
if (ev.
|
|
71
|
-
await
|
|
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
|
|
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
|
|
58
|
-
if (isFunction(
|
|
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
|
-
|
|
61
|
+
result = result.bind(this.target);
|
|
61
62
|
}
|
|
62
|
-
return
|
|
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
|
package/src/service/metadata.ts
CHANGED
|
@@ -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;
|
package/src/service/root.ts
CHANGED
|
@@ -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.
|
|
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(
|
|
49
|
-
prev = new Map(this.#classes.get(
|
|
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(
|
|
55
|
-
this.#classes.set(
|
|
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(
|
|
77
|
+
this.#classes.get(sourceFile)!.delete(k);
|
|
66
78
|
} else {
|
|
67
|
-
this.#classes.get(
|
|
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
|
-
|
|
120
|
-
|
|
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
|
-
|
|
123
|
-
|
|
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
|