@travetto/registry 6.0.1 → 7.0.0-rc.1
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 +55 -27
- package/__index__.ts +3 -4
- package/package.json +4 -4
- package/src/service/registry.ts +166 -0
- package/src/service/store.ts +103 -0
- package/src/service/types.ts +40 -0
- package/src/source/class-source.ts +30 -11
- package/src/source/method-source.ts +5 -5
- package/src/types.ts +4 -5
- package/src/registry.ts +0 -209
- package/src/service/metadata.ts +0 -192
- package/src/service/root.ts +0 -32
package/README.md
CHANGED
|
@@ -20,20 +20,21 @@ Registration, within the framework flows throw two main use cases:
|
|
|
20
20
|
|
|
21
21
|
### Initial Flows
|
|
22
22
|
The primary flow occurs on initialization of the application. At that point, the module will:
|
|
23
|
-
1. Initialize [
|
|
23
|
+
1. Initialize [Registry](https://github.com/travetto/travetto/tree/main/module/registry/src/service/registry.ts#L9) 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
|
-
1. When all files are processed, the [
|
|
25
|
+
1. When all files are processed, the [Registry](https://github.com/travetto/travetto/tree/main/module/registry/src/service/registry.ts#L9) is finished, and it will signal to anything waiting on registered data that its free to use it.
|
|
26
26
|
|
|
27
27
|
This flow ensures all files are loaded and processed before application starts. A sample registry could like:
|
|
28
28
|
|
|
29
29
|
**Code: Sample Registry**
|
|
30
30
|
```typescript
|
|
31
31
|
import { Class } from '@travetto/runtime';
|
|
32
|
-
import {
|
|
32
|
+
import { ChangeEvent, RegistryAdapter, RegistryIndex, RegistryIndexStore, Registry } from '@travetto/registry';
|
|
33
33
|
|
|
34
34
|
interface Group {
|
|
35
35
|
class: Class;
|
|
36
36
|
name: string;
|
|
37
|
+
children: Child[];
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
interface Child {
|
|
@@ -41,41 +42,68 @@ interface Child {
|
|
|
41
42
|
method: Function;
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
/**
|
|
46
|
+
* The adapter to handle mapping/modeling a specific class
|
|
47
|
+
*/
|
|
48
|
+
class SampleRegistryAdapter implements RegistryAdapter<Group> {
|
|
49
|
+
|
|
50
|
+
#class: Class;
|
|
51
|
+
#config: Group;
|
|
52
|
+
|
|
53
|
+
constructor(cls: Class) {
|
|
54
|
+
this.#class = cls;
|
|
55
|
+
}
|
|
47
56
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
throw new Error('Invalid Group');
|
|
57
|
+
register(...data: Partial<Partial<Group>>[]): Group {
|
|
58
|
+
for (const d of data) {
|
|
59
|
+
Object.assign(this.#config, {
|
|
60
|
+
...d,
|
|
61
|
+
children: [
|
|
62
|
+
...(this.#config?.children ?? []),
|
|
63
|
+
...(d.children ?? [])
|
|
64
|
+
]
|
|
65
|
+
});
|
|
58
66
|
}
|
|
67
|
+
return this.#config;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
registerChild(method: Function, name: string): void {
|
|
71
|
+
this.register({ children: [{ method, name }] });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
finalize?(parent?: Partial<Group> | undefined): void {
|
|
75
|
+
// Nothing to do
|
|
59
76
|
}
|
|
60
77
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
78
|
+
get(): Group {
|
|
79
|
+
return this.#config;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Basic Index that handles cross-class activity
|
|
85
|
+
*/
|
|
86
|
+
export class SampleRegistryIndex implements RegistryIndex {
|
|
87
|
+
static #instance = Registry.registerIndex(SampleRegistryIndex);
|
|
88
|
+
|
|
89
|
+
static getForRegister(cls: Class, allowFinalized = false): SampleRegistryAdapter {
|
|
90
|
+
return this.#instance.store.getForRegister(cls, allowFinalized);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
store = new RegistryIndexStore(SampleRegistryAdapter);
|
|
94
|
+
|
|
95
|
+
process(events: ChangeEvent<Class>[]): void {
|
|
96
|
+
// Nothing to do
|
|
69
97
|
}
|
|
70
98
|
}
|
|
71
99
|
```
|
|
72
100
|
|
|
73
|
-
The registry is a [
|
|
101
|
+
The registry index is a [RegistryIndex](https://github.com/travetto/travetto/tree/main/module/registry/src/service/types.ts#L36) 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.
|
|
74
102
|
|
|
75
103
|
### Live Flow
|
|
76
104
|
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.
|
|
77
105
|
|
|
78
|
-
As the [DynamicFileLoader](https://github.com/travetto/travetto/tree/main/module/registry/src/internal/file-loader.ts#L17) notifies that a file has been changed, the [
|
|
106
|
+
As the [DynamicFileLoader](https://github.com/travetto/travetto/tree/main/module/registry/src/internal/file-loader.ts#L17) notifies that a file has been changed, the [Registry](https://github.com/travetto/travetto/tree/main/module/registry/src/service/registry.ts#L9) will pick it up, and process it accordingly.
|
|
79
107
|
|
|
80
108
|
## Supporting Metadata
|
|
81
109
|
As mentioned in [Manifest](https://github.com/travetto/travetto/tree/main/module/manifest#readme "Support for project indexing, manifesting, along with file watching")'s readme, the framework produces hashes of methods, classes, and functions, to allow for detecting changes to individual parts of the codebase. During the live flow, various registries will inspect this information to determine if action should be taken.
|
|
@@ -116,7 +144,7 @@ As mentioned in [Manifest](https://github.com/travetto/travetto/tree/main/module
|
|
|
116
144
|
const nextHash = describeFunction(next.get(k)!)?.hash;
|
|
117
145
|
if (prevHash !== nextHash) {
|
|
118
146
|
changes += 1;
|
|
119
|
-
this.emit({ type: 'changed', curr: next.get(k)!, prev: prev.get(k) });
|
|
147
|
+
this.emit({ type: 'changed', curr: next.get(k)!, prev: prev.get(k)! });
|
|
120
148
|
}
|
|
121
149
|
}
|
|
122
150
|
}
|
package/__index__.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
export * from './src/registry.ts';
|
|
2
|
-
export * from './src/service/
|
|
3
|
-
export * from './src/service/
|
|
1
|
+
export * from './src/service/registry.ts';
|
|
2
|
+
export * from './src/service/types.ts';
|
|
3
|
+
export * from './src/service/store.ts';
|
|
4
4
|
export * from './src/proxy.ts';
|
|
5
|
-
export * from './src/registry.ts';
|
|
6
5
|
export * from './src/source/class-source.ts';
|
|
7
6
|
export * from './src/source/method-source.ts';
|
|
8
7
|
export * from './src/types.ts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/registry",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0-rc.1",
|
|
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": "^
|
|
30
|
+
"@travetto/runtime": "^7.0.0-rc.1"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@travetto/cli": "^
|
|
34
|
-
"@travetto/transformer": "^
|
|
33
|
+
"@travetto/cli": "^7.0.0-rc.1",
|
|
34
|
+
"@travetto/transformer": "^7.0.0-rc.1"
|
|
35
35
|
},
|
|
36
36
|
"peerDependenciesMeta": {
|
|
37
37
|
"@travetto/transformer": {
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { AppError, castTo, Class, Util } from '@travetto/runtime';
|
|
3
|
+
|
|
4
|
+
import { ClassSource } from '../source/class-source';
|
|
5
|
+
import { ChangeEvent } from '../types';
|
|
6
|
+
import { MethodSource } from '../source/method-source';
|
|
7
|
+
import { RegistryIndex, RegistryIndexClass, EXPIRED_CLASS } from './types';
|
|
8
|
+
|
|
9
|
+
class $Registry {
|
|
10
|
+
|
|
11
|
+
#resolved = false;
|
|
12
|
+
#initialized?: Promise<unknown>;
|
|
13
|
+
trace = false;
|
|
14
|
+
#uid = Util.uuid();
|
|
15
|
+
|
|
16
|
+
// Lookups
|
|
17
|
+
#indexes = new Map<RegistryIndexClass, RegistryIndex>();
|
|
18
|
+
#indexOrder: RegistryIndexClass[] = [];
|
|
19
|
+
|
|
20
|
+
// Eventing
|
|
21
|
+
#classSource = new ClassSource();
|
|
22
|
+
#methodSource?: MethodSource;
|
|
23
|
+
#emitter = new EventEmitter<{ event: [ChangeEvent<Class>] }>();
|
|
24
|
+
|
|
25
|
+
#removeItems(classes: Class[]): void {
|
|
26
|
+
for (const cls of classes) {
|
|
27
|
+
for (const idx of this.#indexOrder) {
|
|
28
|
+
this.instance(idx).store.remove(cls);
|
|
29
|
+
}
|
|
30
|
+
// Tag expired classes
|
|
31
|
+
Object.assign(cls, { [EXPIRED_CLASS]: true });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#finalizeItems(classes: Class[]): void {
|
|
36
|
+
for (const idx of this.#indexOrder) {
|
|
37
|
+
const inst = this.instance(idx);
|
|
38
|
+
for (const cls of classes) {
|
|
39
|
+
if (inst.store.has(cls) && !inst.store.finalized(cls)) {
|
|
40
|
+
if (inst.finalize) {
|
|
41
|
+
inst.finalize(cls);
|
|
42
|
+
} else {
|
|
43
|
+
inst.store.finalize(cls);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
finalizeForIndex(indexCls: RegistryIndexClass): void {
|
|
51
|
+
const inst = this.instance(indexCls);
|
|
52
|
+
this.#finalizeItems(inst.store.getClasses());
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
process(events: ChangeEvent<Class>[]): void {
|
|
56
|
+
this.#finalizeItems(events.filter(ev => 'curr' in ev).map(ev => ev.curr));
|
|
57
|
+
|
|
58
|
+
for (const indexCls of this.#indexOrder) { // Visit every index, in order
|
|
59
|
+
const inst = this.instance(indexCls);
|
|
60
|
+
const matched = events.filter(e => inst.store.has('curr' in e ? e.curr : e.prev!));
|
|
61
|
+
if (matched.length) {
|
|
62
|
+
inst.process(matched);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
Util.queueMacroTask().then(() => {
|
|
67
|
+
this.#removeItems(events.filter(ev => 'prev' in ev).map(ev => ev.prev!));
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
for (const e of events) {
|
|
71
|
+
this.#emitter.emit('event', e);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Run initialization
|
|
77
|
+
*/
|
|
78
|
+
async #init(): Promise<void> {
|
|
79
|
+
try {
|
|
80
|
+
this.#resolved = false;
|
|
81
|
+
|
|
82
|
+
if (this.trace) {
|
|
83
|
+
console.debug('Initializing', { uid: this.#uid });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const added = await this.#classSource.init();
|
|
87
|
+
this.process(added.map(cls => ({ type: 'added', curr: cls })));
|
|
88
|
+
this.#classSource.on(e => this.process([e]));
|
|
89
|
+
} finally {
|
|
90
|
+
this.#resolved = true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Verify initialized state
|
|
96
|
+
*/
|
|
97
|
+
verifyInitialized(): void {
|
|
98
|
+
if (!this.#resolved) {
|
|
99
|
+
throw new AppError('Registry not initialized, call init() first');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Register a new index
|
|
105
|
+
*/
|
|
106
|
+
registerIndex<T extends RegistryIndexClass>(indexCls: T): InstanceType<T> {
|
|
107
|
+
if (!this.#indexes.has(indexCls)) {
|
|
108
|
+
this.#indexes.set(indexCls, new indexCls());
|
|
109
|
+
this.#indexOrder.push(indexCls);
|
|
110
|
+
}
|
|
111
|
+
return castTo(this.#indexes.get(indexCls));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Initialize, with a built-in latch to prevent concurrent initializations
|
|
116
|
+
*/
|
|
117
|
+
async init(): Promise<unknown> {
|
|
118
|
+
if (this.trace && this.#initialized) {
|
|
119
|
+
console.trace('Trying to re-initialize', { uid: this.#uid, initialized: !!this.#initialized });
|
|
120
|
+
}
|
|
121
|
+
return this.#initialized ??= this.#init();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
instance<T extends RegistryIndexClass>(indexCls: T): InstanceType<T> {
|
|
125
|
+
return castTo(this.#indexes.get(indexCls));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Listen for changes
|
|
130
|
+
*/
|
|
131
|
+
onClassChange(handler: (event: ChangeEvent<Class>) => void, matches?: RegistryIndexClass): void {
|
|
132
|
+
if (!matches) {
|
|
133
|
+
this.#emitter.on('event', handler);
|
|
134
|
+
} else {
|
|
135
|
+
const inst = this.instance(matches);
|
|
136
|
+
this.#emitter.on('event', (event) => {
|
|
137
|
+
if (inst.store.has('curr' in event ? event.curr : event.prev!)) {
|
|
138
|
+
handler(event);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
onNonClassChanges(handler: (file: string) => void): void {
|
|
145
|
+
this.#classSource.onNonClassChanges(handler);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
onMethodChange(
|
|
149
|
+
handler: (event: ChangeEvent<[Class, Function]>) => void,
|
|
150
|
+
matches?: RegistryIndexClass,
|
|
151
|
+
): void {
|
|
152
|
+
const src = this.#methodSource ??= new MethodSource(this.#classSource);
|
|
153
|
+
if (!matches) {
|
|
154
|
+
src.on(handler);
|
|
155
|
+
} else {
|
|
156
|
+
const inst = this.instance(matches);
|
|
157
|
+
src.on((event) => {
|
|
158
|
+
if (inst.store.has('curr' in event ? event.curr[0] : event.prev[0])) {
|
|
159
|
+
handler(event);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export const Registry = new $Registry();
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Any, AppError, castTo, Class, getParentClass, Runtime } from '@travetto/runtime';
|
|
2
|
+
|
|
3
|
+
import { EXPIRED_CLASS, RegistrationMethods, RegistryAdapter } from './types';
|
|
4
|
+
|
|
5
|
+
function ExchangeExpired<R = unknown>() {
|
|
6
|
+
return function (
|
|
7
|
+
target: Any,
|
|
8
|
+
propertyKey: string | symbol,
|
|
9
|
+
descriptor: TypedPropertyDescriptor<(this: RegistryIndexStore, cls: Class) => R>
|
|
10
|
+
): void {
|
|
11
|
+
if (Runtime.dynamic) {
|
|
12
|
+
const original = descriptor.value!;
|
|
13
|
+
descriptor.value = function (this: RegistryIndexStore, cls: Class): R {
|
|
14
|
+
const resolved = EXPIRED_CLASS in cls ? this.getClassById(cls.Ⲑid) : cls;
|
|
15
|
+
return original.apply(this, [resolved]);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Base registry index implementation
|
|
23
|
+
*/
|
|
24
|
+
export class RegistryIndexStore<A extends RegistryAdapter<{}> = RegistryAdapter<{}>> {
|
|
25
|
+
|
|
26
|
+
// Core data
|
|
27
|
+
#adapters = new Map<Class, A>();
|
|
28
|
+
#idToCls = new Map<string, Class>();
|
|
29
|
+
#adapterCls: new (cls: Class) => A;
|
|
30
|
+
#finalized = new Map<Class, boolean>();
|
|
31
|
+
|
|
32
|
+
constructor(adapterCls: new (cls: Class) => A) {
|
|
33
|
+
this.#adapterCls = adapterCls;
|
|
34
|
+
this.getClassById = this.getClassById.bind(this);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getClasses(): Class[] {
|
|
38
|
+
return Array.from(this.#adapters.keys());
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getClassById(id: string): Class {
|
|
42
|
+
return this.#idToCls.get(id)!;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
finalize(cls: Class, parentConfig?: ReturnType<A['get']>): void {
|
|
46
|
+
if (!parentConfig) {
|
|
47
|
+
const parentClass = getParentClass(cls);
|
|
48
|
+
parentConfig = castTo(parentClass && this.has(parentClass) ? this.get(parentClass).get() : undefined);
|
|
49
|
+
}
|
|
50
|
+
this.adapter(cls).finalize?.(parentConfig);
|
|
51
|
+
this.#finalized.set(cls, true);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
remove(cls: Class): void {
|
|
55
|
+
this.#adapters.delete(cls);
|
|
56
|
+
this.#finalized.delete(cls);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@ExchangeExpired()
|
|
60
|
+
has(cls: Class): boolean {
|
|
61
|
+
return this.#adapters.has(cls);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@ExchangeExpired()
|
|
65
|
+
adapter(cls: Class): A {
|
|
66
|
+
if (!this.#adapters.has(cls)!) {
|
|
67
|
+
const adapter = new this.#adapterCls(cls);
|
|
68
|
+
this.#adapters.set(cls, adapter);
|
|
69
|
+
this.#idToCls.set(cls.Ⲑid, cls);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return castTo(this.#adapters.get(cls));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@ExchangeExpired()
|
|
76
|
+
getForRegister(cls: Class, allowFinalized = false): A {
|
|
77
|
+
if (this.#finalized.get(cls) && !allowFinalized) {
|
|
78
|
+
throw new AppError(`Class ${cls.Ⲑid} is already finalized`);
|
|
79
|
+
}
|
|
80
|
+
return this.adapter(cls);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@ExchangeExpired()
|
|
84
|
+
get(cls: Class): Omit<A, RegistrationMethods> {
|
|
85
|
+
if (!this.has(cls)) {
|
|
86
|
+
throw new AppError(`Class ${cls.Ⲑid} is not registered for ${this.#adapterCls.Ⲑid}`);
|
|
87
|
+
}
|
|
88
|
+
return this.adapter(cls);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@ExchangeExpired()
|
|
92
|
+
getOptional(cls: Class): Omit<A, RegistrationMethods> | undefined {
|
|
93
|
+
if (!this.has(cls)) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
return this.adapter(cls);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@ExchangeExpired()
|
|
100
|
+
finalized(cls: Class): boolean {
|
|
101
|
+
return this.#finalized.has(cls);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Class } from '@travetto/runtime';
|
|
2
|
+
import { ChangeEvent } from '../types';
|
|
3
|
+
|
|
4
|
+
export type RegistrationMethods = `register${string}` | `finalize${string}`;
|
|
5
|
+
|
|
6
|
+
export const EXPIRED_CLASS = Symbol();
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Interface for registry adapters to implement
|
|
10
|
+
*/
|
|
11
|
+
export interface RegistryAdapter<C extends {} = {}> {
|
|
12
|
+
register(...data: Partial<C>[]): C;
|
|
13
|
+
finalize?(parent?: C): void;
|
|
14
|
+
get(): C;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type RegistryIndexClass = {
|
|
18
|
+
new(): RegistryIndex;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Simple store interface for registry indexes
|
|
23
|
+
*/
|
|
24
|
+
export interface RegistrySimpleStore {
|
|
25
|
+
has(cls: Class): boolean;
|
|
26
|
+
finalize(cls: Class): void;
|
|
27
|
+
finalized(cls: Class): boolean;
|
|
28
|
+
remove(cls: Class): void;
|
|
29
|
+
getClasses(): Class[];
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Registry index definition
|
|
34
|
+
* @concrete
|
|
35
|
+
*/
|
|
36
|
+
export interface RegistryIndex {
|
|
37
|
+
store: RegistrySimpleStore;
|
|
38
|
+
process(events: ChangeEvent<Class>[]): void;
|
|
39
|
+
finalize?(cls: Class): void;
|
|
40
|
+
}
|
|
@@ -17,7 +17,11 @@ function isClass(cls: Function): cls is Class {
|
|
|
17
17
|
export class ClassSource implements ChangeSource<Class> {
|
|
18
18
|
|
|
19
19
|
#classes = new Map<string, Map<string, Class>>();
|
|
20
|
-
#emitter = new EventEmitter
|
|
20
|
+
#emitter = new EventEmitter<{
|
|
21
|
+
change: [ChangeEvent<Class>];
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
23
|
+
'unchanged-import': [string];
|
|
24
|
+
}>();
|
|
21
25
|
#listening: Promise<void> | undefined;
|
|
22
26
|
|
|
23
27
|
/**
|
|
@@ -28,8 +32,9 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
28
32
|
/**
|
|
29
33
|
* Flush classes
|
|
30
34
|
*/
|
|
31
|
-
#flush():
|
|
32
|
-
|
|
35
|
+
#flush(): Class[] {
|
|
36
|
+
const flushed = flushPendingFunctions().filter(isClass);
|
|
37
|
+
for (const cls of flushed) {
|
|
33
38
|
const src = Runtime.getSourceFile(cls);
|
|
34
39
|
if (!this.#classes.has(src)) {
|
|
35
40
|
this.#classes.set(src, new Map());
|
|
@@ -37,6 +42,7 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
37
42
|
this.#classes.get(src)!.set(cls.Ⲑid, cls);
|
|
38
43
|
this.emit({ type: 'added', curr: cls });
|
|
39
44
|
}
|
|
45
|
+
return flushed;
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
#removeFile(file: string): void {
|
|
@@ -85,7 +91,7 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
85
91
|
const nextHash = describeFunction(next.get(k)!)?.hash;
|
|
86
92
|
if (prevHash !== nextHash) {
|
|
87
93
|
changes += 1;
|
|
88
|
-
this.emit({ type: 'changed', curr: next.get(k)!, prev: prev.get(k) });
|
|
94
|
+
this.emit({ type: 'changed', curr: next.get(k)!, prev: prev.get(k)! });
|
|
89
95
|
}
|
|
90
96
|
}
|
|
91
97
|
}
|
|
@@ -118,7 +124,11 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
118
124
|
*/
|
|
119
125
|
emit(e: ChangeEvent<Class>): void {
|
|
120
126
|
if (this.trace) {
|
|
121
|
-
console.debug('Emitting change', {
|
|
127
|
+
console.debug('Emitting change', {
|
|
128
|
+
type: e.type,
|
|
129
|
+
curr: (e.type !== 'removing' ? e.curr?.Ⲑid : undefined),
|
|
130
|
+
prev: (e.type !== 'added' ? e.prev?.Ⲑid : undefined)
|
|
131
|
+
});
|
|
122
132
|
}
|
|
123
133
|
this.#emitter.emit('change', e);
|
|
124
134
|
}
|
|
@@ -126,7 +136,7 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
126
136
|
/**
|
|
127
137
|
* Initialize
|
|
128
138
|
*/
|
|
129
|
-
async init(): Promise<
|
|
139
|
+
async init(): Promise<Class[]> {
|
|
130
140
|
if (Runtime.dynamic && !this.#listening) {
|
|
131
141
|
this.#listening = (async (): Promise<void> => {
|
|
132
142
|
for await (const ev of await DynamicFileLoader.listen()) {
|
|
@@ -147,10 +157,12 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
147
157
|
for (const entry of RuntimeIndex.find({
|
|
148
158
|
module: (m) => {
|
|
149
159
|
const role = Env.TRV_ROLE.val;
|
|
150
|
-
return
|
|
151
|
-
|
|
152
|
-
(
|
|
153
|
-
|
|
160
|
+
return role !== 'test' && // Skip all modules when in test
|
|
161
|
+
m.roles.includes('std') &&
|
|
162
|
+
(
|
|
163
|
+
!Runtime.production || m.prod ||
|
|
164
|
+
(role === 'doc' && m.roles.includes(role))
|
|
165
|
+
);
|
|
154
166
|
},
|
|
155
167
|
folder: f => f === 'src' || f === '$index'
|
|
156
168
|
})) {
|
|
@@ -158,7 +170,7 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
158
170
|
}
|
|
159
171
|
|
|
160
172
|
// Flush all load events
|
|
161
|
-
this.#flush();
|
|
173
|
+
return this.#flush();
|
|
162
174
|
}
|
|
163
175
|
|
|
164
176
|
/**
|
|
@@ -168,6 +180,13 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
168
180
|
this.#emitter.on('change', callback);
|
|
169
181
|
}
|
|
170
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Add callback for change events
|
|
185
|
+
*/
|
|
186
|
+
off(callback: ChangeHandler<Class>): void {
|
|
187
|
+
this.#emitter.off('change', callback);
|
|
188
|
+
}
|
|
189
|
+
|
|
171
190
|
/**
|
|
172
191
|
* Add callback for when a import is changed, but emits no class changes
|
|
173
192
|
*/
|
|
@@ -29,16 +29,16 @@ export class MethodSource implements ChangeSource<[Class, Function]> {
|
|
|
29
29
|
* On a class being emitted, check methods
|
|
30
30
|
*/
|
|
31
31
|
onClassEvent(e: ChangeEvent<Class>): void {
|
|
32
|
-
const next = describeFunction(e.curr!)?.methods ?? {};
|
|
33
|
-
const prev = describeFunction(e.prev!)?.methods ?? {};
|
|
32
|
+
const next = (e.type !== 'removing' ? describeFunction(e.curr!)?.methods : null) ?? {};
|
|
33
|
+
const prev = (e.type !== 'added' ? describeFunction(e.prev!)?.methods : null) ?? {};
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Go through each method, comparing hashes. To see added/removed and changed
|
|
37
37
|
*/
|
|
38
38
|
for (const k of Object.keys(next)) {
|
|
39
|
-
if (!prev[k] || !e.
|
|
39
|
+
if ((!prev[k] || !('prev' in e)) && e.type !== 'removing') {
|
|
40
40
|
this.emit({ type: 'added', curr: [e.curr!, e.curr!.prototype[k]] });
|
|
41
|
-
} else if (next[k].hash !== prev[k].hash && e.
|
|
41
|
+
} else if (next[k].hash !== prev[k].hash && e.type === 'changed') {
|
|
42
42
|
// FIXME: Why is e.prev undefined sometimes?
|
|
43
43
|
this.emit({ type: 'changed', curr: [e.curr, e.curr.prototype[k]], prev: [e.prev, e.prev.prototype[k]] });
|
|
44
44
|
} else {
|
|
@@ -47,7 +47,7 @@ export class MethodSource implements ChangeSource<[Class, Function]> {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
for (const k of Object.keys(prev)) {
|
|
50
|
-
if (!next[k] && e.
|
|
50
|
+
if (!next[k] && e.type !== 'added') {
|
|
51
51
|
this.emit({ type: 'removing', prev: [e.prev, e.prev.prototype[k]] });
|
|
52
52
|
}
|
|
53
53
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A change event
|
|
3
3
|
*/
|
|
4
|
-
export
|
|
5
|
-
type: 'changed'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
4
|
+
export type ChangeEvent<T> =
|
|
5
|
+
{ type: 'changed', prev: T, curr: T } |
|
|
6
|
+
{ type: 'added', curr: T } |
|
|
7
|
+
{ type: 'removing', prev: T };
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* Change handler
|
package/src/registry.ts
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'node:events';
|
|
2
|
-
import { Class, Env } from '@travetto/runtime';
|
|
3
|
-
import { ChangeSource, ChangeEvent, ChangeHandler } from './types.ts';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Base registry class, designed to listen to changes over time
|
|
7
|
-
*/
|
|
8
|
-
export abstract class Registry implements ChangeSource<Class> {
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Has the registry been resolved
|
|
12
|
-
*/
|
|
13
|
-
#resolved: boolean;
|
|
14
|
-
/**
|
|
15
|
-
* Initializing promises
|
|
16
|
-
*/
|
|
17
|
-
#initialized?: Promise<unknown>;
|
|
18
|
-
/**
|
|
19
|
-
* Event emitter, to broadcast event changes
|
|
20
|
-
*/
|
|
21
|
-
#emitter = new EventEmitter();
|
|
22
|
-
/**
|
|
23
|
-
* Dependent registries
|
|
24
|
-
*/
|
|
25
|
-
#dependents: Registry[] = [];
|
|
26
|
-
/**
|
|
27
|
-
* Parent registries
|
|
28
|
-
*/
|
|
29
|
-
#parents: ChangeSource<Class>[] = [];
|
|
30
|
-
/**
|
|
31
|
-
* Unique identifier
|
|
32
|
-
*/
|
|
33
|
-
#uid: string;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Are we in a mode that should have enhanced debug info
|
|
37
|
-
*/
|
|
38
|
-
trace = Env.DEBUG.val?.includes('@travetto/registry');
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Creates a new registry, with it's parents specified
|
|
42
|
-
*/
|
|
43
|
-
constructor(...parents: ChangeSource<Class>[]) {
|
|
44
|
-
this.#uid = `${this.constructor.name}_${Date.now()}`;
|
|
45
|
-
this.#parents = parents;
|
|
46
|
-
|
|
47
|
-
if (this.#parents.length) {
|
|
48
|
-
// Have the child listen to the parents
|
|
49
|
-
for (const parent of this.#parents) {
|
|
50
|
-
this.listen(parent);
|
|
51
|
-
if (parent instanceof Registry) {
|
|
52
|
-
parent.#dependents.push(this);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Run initialization
|
|
60
|
-
*/
|
|
61
|
-
async #runInit(): Promise<void> {
|
|
62
|
-
try {
|
|
63
|
-
this.#resolved = false;
|
|
64
|
-
if (this.trace) {
|
|
65
|
-
console.debug('Initializing', { id: this.constructor.Ⲑid, uid: this.#uid });
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Handle top level when dealing with non-registry
|
|
69
|
-
const waitFor = this.#parents.filter(x => !(x instanceof Registry));
|
|
70
|
-
await Promise.all(waitFor.map(x => x.init()));
|
|
71
|
-
|
|
72
|
-
const classes = await this.initialInstall();
|
|
73
|
-
|
|
74
|
-
if (classes) {
|
|
75
|
-
for (const cls of classes) {
|
|
76
|
-
this.install(cls, { type: 'added', curr: cls });
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
await Promise.all(this.#dependents.map(x => x.init()));
|
|
81
|
-
} finally {
|
|
82
|
-
this.#resolved = true;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
get resolved(): boolean {
|
|
87
|
-
return this.#resolved;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Return list of classes for the initial installation
|
|
92
|
-
*/
|
|
93
|
-
initialInstall(): Class[] {
|
|
94
|
-
return [];
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Verify initialized state
|
|
99
|
-
*/
|
|
100
|
-
verifyInitialized(): void {
|
|
101
|
-
if (!this.#resolved) {
|
|
102
|
-
throw new Error(`${this.constructor.name} has not been initialized, you probably need to call RootRegistry.init()`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Initialize, with a built-in latch to prevent concurrent initializations
|
|
108
|
-
*/
|
|
109
|
-
async init(): Promise<unknown> {
|
|
110
|
-
if (this.trace) {
|
|
111
|
-
console.debug('Trying to initialize', { id: this.constructor.Ⲑid, uid: this.#uid, initialized: !!this.#initialized });
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (!this.#initialized) {
|
|
115
|
-
this.#initialized = this.#runInit();
|
|
116
|
-
}
|
|
117
|
-
return this.#initialized;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
parent<T extends ChangeSource<Class>>(type: Class<T>): T | undefined {
|
|
121
|
-
return this.#parents.find((dep: unknown): dep is T => dep instanceof type);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* When an installation event occurs
|
|
126
|
-
*/
|
|
127
|
-
onInstall?(cls: Class, e: ChangeEvent<Class>): void;
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* When an un-installation event occurs
|
|
131
|
-
*/
|
|
132
|
-
onUninstall?(cls: Class, e: ChangeEvent<Class>): void;
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Uninstall a class or list of classes
|
|
136
|
-
*/
|
|
137
|
-
uninstall(classes: Class | Class[], e: ChangeEvent<Class>): void {
|
|
138
|
-
if (!Array.isArray(classes)) {
|
|
139
|
-
classes = [classes];
|
|
140
|
-
}
|
|
141
|
-
for (const cls of classes) {
|
|
142
|
-
this.onUninstall?.(cls, e);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Install a class or a list of classes
|
|
148
|
-
*/
|
|
149
|
-
install(classes: Class | Class[], e: ChangeEvent<Class>): void {
|
|
150
|
-
if (!Array.isArray(classes)) {
|
|
151
|
-
classes = [classes];
|
|
152
|
-
}
|
|
153
|
-
for (const cls of classes) {
|
|
154
|
-
this.onInstall?.(cls, e);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Listen for events from the parent
|
|
160
|
-
*/
|
|
161
|
-
onEvent(event: ChangeEvent<Class>): void {
|
|
162
|
-
if (this.trace) {
|
|
163
|
-
console.debug('Received', { id: this.constructor.Ⲑid, type: event.type, targetId: (event.curr ?? event.prev)!.Ⲑid });
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
switch (event.type) {
|
|
167
|
-
case 'removing':
|
|
168
|
-
this.uninstall(event.prev!, event);
|
|
169
|
-
break;
|
|
170
|
-
case 'added':
|
|
171
|
-
this.install(event.curr!, event);
|
|
172
|
-
break;
|
|
173
|
-
case 'changed':
|
|
174
|
-
this.uninstall(event.prev!, event);
|
|
175
|
-
this.install(event.curr!, event);
|
|
176
|
-
break;
|
|
177
|
-
default:
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Emit a new event
|
|
184
|
-
*/
|
|
185
|
-
emit(e: ChangeEvent<Class>): void {
|
|
186
|
-
this.#emitter.emit('change', e);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Register additional listeners
|
|
191
|
-
*/
|
|
192
|
-
on<T>(callback: ChangeHandler<Class<T>>): void {
|
|
193
|
-
this.#emitter.on('change', callback);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Remove listeners
|
|
198
|
-
*/
|
|
199
|
-
off<T>(callback: ChangeHandler<Class<T>>): void {
|
|
200
|
-
this.#emitter.off('change', callback);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Connect changes sources
|
|
205
|
-
*/
|
|
206
|
-
listen(source: ChangeSource<Class>): void {
|
|
207
|
-
source.on(e => this.onEvent(e));
|
|
208
|
-
}
|
|
209
|
-
}
|
package/src/service/metadata.ts
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
/* eslint @typescript-eslint/no-unused-vars: ["error", { "args": "none"} ] */
|
|
2
|
-
import { Class } from '@travetto/runtime';
|
|
3
|
-
|
|
4
|
-
import { Registry } from '../registry.ts';
|
|
5
|
-
import { ChangeEvent } from '../types.ts';
|
|
6
|
-
|
|
7
|
-
function id(cls: string | Class): string {
|
|
8
|
-
return cls && typeof cls !== 'string' ? cls.Ⲑid : cls;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Metadata registry
|
|
13
|
-
*/
|
|
14
|
-
export abstract class MetadataRegistry<C extends { class: Class }, M = unknown, F = Function> extends Registry {
|
|
15
|
-
|
|
16
|
-
static id = id;
|
|
17
|
-
/**
|
|
18
|
-
* Classes pending removal
|
|
19
|
-
*/
|
|
20
|
-
expired = new Map<string, C>();
|
|
21
|
-
/**
|
|
22
|
-
* Classes pending creation
|
|
23
|
-
*/
|
|
24
|
-
pending = new Map<string, Partial<C>>();
|
|
25
|
-
/**
|
|
26
|
-
* Fields pending creation
|
|
27
|
-
*/
|
|
28
|
-
pendingFields = new Map<string, Map<F, Partial<M>>>();
|
|
29
|
-
/**
|
|
30
|
-
* Active items
|
|
31
|
-
*/
|
|
32
|
-
entries = new Map<string, C>();
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Code to call when the installation is finalized
|
|
36
|
-
*/
|
|
37
|
-
abstract onInstallFinalize<T>(cls: Class<T>): C;
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Code to call when uninstall is finalized
|
|
41
|
-
*/
|
|
42
|
-
onUninstallFinalize<T>(cls: Class<T>): void {
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Create a pending class. Items are pending until the registry is activated
|
|
48
|
-
*/
|
|
49
|
-
abstract createPending(cls: Class): Partial<C>;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Is class found by id or by Class
|
|
53
|
-
*/
|
|
54
|
-
has(cls: string | Class): boolean {
|
|
55
|
-
return this.entries.has(id(cls));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Get class by id or by Class
|
|
60
|
-
*/
|
|
61
|
-
get(cls: string | Class): C {
|
|
62
|
-
return this.entries.get(id(cls))!;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Retrieve the class that is being removed
|
|
67
|
-
*/
|
|
68
|
-
getExpired(cls: string | Class): C {
|
|
69
|
-
return this.expired.get(id(cls))!;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Is there a class that is expiring
|
|
74
|
-
*/
|
|
75
|
-
hasExpired(cls: string | Class): boolean {
|
|
76
|
-
return this.expired.has(id(cls));
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Is there a pending state for the class
|
|
81
|
-
*/
|
|
82
|
-
hasPending(cls: string | Class): boolean {
|
|
83
|
-
return this.pending.has(id(cls));
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Get list of all classes that have been registered
|
|
88
|
-
*/
|
|
89
|
-
getClasses(): Class[] {
|
|
90
|
-
return Array.from(this.entries.values()).map(x => x.class);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Trigger initial install, moves pending to finalized (active)
|
|
95
|
-
*/
|
|
96
|
-
override initialInstall(): Class[] {
|
|
97
|
-
return Array.from(this.pending.values()).map(x => x.class).filter(x => !!x);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Create a pending field
|
|
102
|
-
*/
|
|
103
|
-
createPendingField(cls: Class, field: F): Partial<M> {
|
|
104
|
-
return {};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Find parent class for a given class object
|
|
109
|
-
*/
|
|
110
|
-
getParentClass(cls: Class): Class | null {
|
|
111
|
-
const parent: Class = Object.getPrototypeOf(cls);
|
|
112
|
-
return parent.name && parent !== Object ? parent : null;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Get or create a pending class
|
|
117
|
-
*/
|
|
118
|
-
getOrCreatePending(cls: Class): Partial<C> {
|
|
119
|
-
const cid = id(cls);
|
|
120
|
-
if (!this.pending.has(cid)) {
|
|
121
|
-
this.pending.set(cid, this.createPending(cls));
|
|
122
|
-
this.pendingFields.set(cid, new Map());
|
|
123
|
-
}
|
|
124
|
-
return this.pending.get(cid)!;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Get or create a pending field
|
|
129
|
-
*/
|
|
130
|
-
getOrCreatePendingField(cls: Class, field: F): Partial<M> {
|
|
131
|
-
this.getOrCreatePending(cls);
|
|
132
|
-
const classId = cls.Ⲑid;
|
|
133
|
-
|
|
134
|
-
if (!this.pendingFields.get(classId)!.has(field)) {
|
|
135
|
-
this.pendingFields.get(classId)!.set(field, this.createPendingField(cls, field));
|
|
136
|
-
}
|
|
137
|
-
return this.pendingFields.get(classId)!.get(field)!;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Register a pending class, with partial config to overlay
|
|
142
|
-
*/
|
|
143
|
-
register(cls: Class, pConfig: Partial<C> = {}): void {
|
|
144
|
-
const conf = this.getOrCreatePending(cls);
|
|
145
|
-
Object.assign(conf, pConfig);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Register a pending field, with partial config to overlay
|
|
150
|
-
*/
|
|
151
|
-
registerField(cls: Class, field: F, pConfig: Partial<M>): void {
|
|
152
|
-
const conf = this.getOrCreatePendingField(cls, field);
|
|
153
|
-
Object.assign(conf, pConfig);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* On an install event, finalize
|
|
158
|
-
*/
|
|
159
|
-
onInstall(cls: Class, e: ChangeEvent<Class>): void {
|
|
160
|
-
const classId = cls.Ⲑid;
|
|
161
|
-
if (this.pending.has(classId) || this.pendingFields.has(classId)) {
|
|
162
|
-
if (this.trace) {
|
|
163
|
-
console.debug('Installing', { service: this.constructor.name, id: classId });
|
|
164
|
-
}
|
|
165
|
-
const result = this.onInstallFinalize(cls);
|
|
166
|
-
this.pendingFields.delete(classId);
|
|
167
|
-
this.pending.delete(classId);
|
|
168
|
-
|
|
169
|
-
this.entries.set(classId, result);
|
|
170
|
-
this.emit(e);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* On an uninstall event, remove
|
|
176
|
-
*/
|
|
177
|
-
onUninstall(cls: Class, e: ChangeEvent<Class>): void {
|
|
178
|
-
const classId = cls.Ⲑid;
|
|
179
|
-
if (this.entries.has(classId)) {
|
|
180
|
-
if (this.trace) {
|
|
181
|
-
console.debug('Uninstalling', { service: this.constructor.name, id: classId });
|
|
182
|
-
}
|
|
183
|
-
this.expired.set(classId, this.entries.get(classId)!);
|
|
184
|
-
this.entries.delete(classId);
|
|
185
|
-
this.onUninstallFinalize(cls);
|
|
186
|
-
if (e.type === 'removing') {
|
|
187
|
-
this.emit(e);
|
|
188
|
-
}
|
|
189
|
-
process.nextTick(() => this.expired.delete(classId));
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
package/src/service/root.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { Class } from '@travetto/runtime';
|
|
2
|
-
|
|
3
|
-
import { Registry } from '../registry.ts';
|
|
4
|
-
import { ClassSource } from '../source/class-source.ts';
|
|
5
|
-
import { ChangeEvent } from '../types.ts';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* The root registry that controls all registries
|
|
9
|
-
*/
|
|
10
|
-
class $RootRegistry extends Registry {
|
|
11
|
-
constructor() {
|
|
12
|
-
super(new ClassSource());
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Send event to all all of the children
|
|
17
|
-
*/
|
|
18
|
-
override async onEvent(e: ChangeEvent<Class>): Promise<void> {
|
|
19
|
-
await super.onEvent(e); // Process event, and
|
|
20
|
-
this.emit(e); // Send to children
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Registers a listener to be notified when a file changes, but no
|
|
25
|
-
* classes are modified
|
|
26
|
-
*/
|
|
27
|
-
onNonClassChanges(handler: (file: string) => void): void {
|
|
28
|
-
this.parent(ClassSource)!.onNonClassChanges(handler);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export const RootRegistry = new $RootRegistry();
|