@travetto/registry 3.4.2 → 4.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 +2 -2
- package/__index__.ts +1 -0
- package/package.json +4 -4
- package/src/decorator.ts +2 -2
- package/src/internal/commonjs-loader.ts +8 -6
- package/src/internal/file-loader.ts +21 -15
- package/src/proxy.ts +137 -0
- package/src/registry.ts +1 -1
- package/src/service/metadata.ts +3 -4
- package/src/source/class-source.ts +15 -13
- package/src/source/method-source.ts +4 -4
package/README.md
CHANGED
|
@@ -112,8 +112,8 @@ As mentioned in [Manifest](https://github.com/travetto/travetto/tree/main/module
|
|
|
112
112
|
changes += 1;
|
|
113
113
|
this.emit({ type: 'added', curr: next.get(k)! });
|
|
114
114
|
} else {
|
|
115
|
-
const prevMeta =
|
|
116
|
-
const nextMeta =
|
|
115
|
+
const prevMeta = RuntimeIndex.getFunctionMetadataFromClass(prev.get(k));
|
|
116
|
+
const nextMeta = RuntimeIndex.getFunctionMetadataFromClass(next.get(k));
|
|
117
117
|
if (prevMeta?.hash !== nextMeta?.hash) {
|
|
118
118
|
changes += 1;
|
|
119
119
|
this.emit({ type: 'changed', curr: next.get(k)!, prev: prev.get(k) });
|
package/__index__.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/registry",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.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/base": "^
|
|
30
|
+
"@travetto/base": "^4.0.0-rc.1"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@travetto/cli": "^
|
|
34
|
-
"@travetto/transformer": "^
|
|
33
|
+
"@travetto/cli": "^4.0.0-rc.1",
|
|
34
|
+
"@travetto/transformer": "^4.0.0-rc.1"
|
|
35
35
|
},
|
|
36
36
|
"peerDependenciesMeta": {
|
|
37
37
|
"@travetto/transformer": {
|
package/src/decorator.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Class } from '@travetto/base';
|
|
2
|
-
import {
|
|
2
|
+
import { RuntimeIndex } from '@travetto/manifest';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Register a class as pending
|
|
@@ -12,7 +12,7 @@ class $PendingRegister {
|
|
|
12
12
|
* Register class as pending
|
|
13
13
|
*/
|
|
14
14
|
add(cls: Class): void {
|
|
15
|
-
const src =
|
|
15
|
+
const src = RuntimeIndex.getFunctionMetadata(cls)!.source;
|
|
16
16
|
if (!this.map.has(src)) {
|
|
17
17
|
const sub: Class[] = [];
|
|
18
18
|
this.map.set(src, sub);
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { Module } from 'module';
|
|
1
|
+
import { Module } from 'node:module';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { RuntimeIndex, path } from '@travetto/manifest';
|
|
4
|
+
import { Env } from '@travetto/base';
|
|
5
|
+
|
|
6
|
+
import { RetargettingProxy } from '../proxy';
|
|
5
7
|
|
|
6
8
|
declare module 'module' {
|
|
7
9
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
@@ -30,7 +32,7 @@ export class DynamicCommonjsLoader {
|
|
|
30
32
|
mod = moduleLoad.apply(null, [request, parent]);
|
|
31
33
|
} catch (err: unknown) {
|
|
32
34
|
const name = Module._resolveFilename!(request, parent);
|
|
33
|
-
if (err instanceof Error &&
|
|
35
|
+
if (err instanceof Error && Env.dynamic && !name.startsWith('test/')) {
|
|
34
36
|
const errMsg = err.message;
|
|
35
37
|
console.debug(`Unable to load ${name}: stubbing out with error proxy.`, errMsg);
|
|
36
38
|
const e = (): never => { throw new Error(errMsg); };
|
|
@@ -41,8 +43,8 @@ export class DynamicCommonjsLoader {
|
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
const fileName = Module._resolveFilename!(request, parent);
|
|
44
|
-
// Only proxy
|
|
45
|
-
if (
|
|
46
|
+
// Only proxy workspace modules
|
|
47
|
+
if (RuntimeIndex.getModuleFromSource(fileName)?.workspace) {
|
|
46
48
|
return proxyModuleLoad ? proxyModuleLoad(fileName, mod) : mod;
|
|
47
49
|
} else {
|
|
48
50
|
return mod;
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { ManifestModuleUtil,
|
|
2
|
-
import {
|
|
1
|
+
import { ManifestModuleUtil, RuntimeIndex, RuntimeContext } from '@travetto/manifest';
|
|
2
|
+
import { watchCompiler, FullWatchEvent, WatchEvent } from '@travetto/base';
|
|
3
3
|
|
|
4
|
-
type WatchHandler = Parameters<CompilerClient['onFileChange']>[0];
|
|
5
|
-
type CompilerWatchEvent = Parameters<WatchHandler>[0];
|
|
6
4
|
interface ModuleLoader {
|
|
7
5
|
init?(): Promise<void>;
|
|
8
6
|
load(file: string): Promise<void>;
|
|
9
7
|
unload(file: string): Promise<void>;
|
|
10
8
|
}
|
|
11
9
|
|
|
10
|
+
type Handler = (ev: WatchEvent) => unknown;
|
|
11
|
+
|
|
12
12
|
const VALID_FILE_TYPES = new Set(['js', 'ts']);
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Listens to file changes, and provides a unified interface for watching file changes, reloading files as needed
|
|
16
16
|
*/
|
|
17
17
|
class $DynamicFileLoader {
|
|
18
|
-
#handlers:
|
|
18
|
+
#handlers: Handler[] = [];
|
|
19
19
|
#loader: ModuleLoader;
|
|
20
20
|
#initialized = false;
|
|
21
21
|
|
|
22
|
-
async dispatch(ev:
|
|
22
|
+
async dispatch(ev: FullWatchEvent): Promise<void> {
|
|
23
23
|
if (ev.action === 'update' || ev.action === 'delete') {
|
|
24
24
|
await this.#loader.unload(ev.output);
|
|
25
25
|
}
|
|
26
26
|
if (ev.action === 'create' || ev.action === 'delete') {
|
|
27
|
-
|
|
27
|
+
RuntimeIndex.reinitForModule(RuntimeContext.main.name);
|
|
28
28
|
}
|
|
29
29
|
if (ev.action === 'create' || ev.action === 'update') {
|
|
30
30
|
await this.#loader.load(ev.output);
|
|
@@ -35,7 +35,7 @@ class $DynamicFileLoader {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
onLoadEvent(handler:
|
|
38
|
+
onLoadEvent(handler: Handler): void {
|
|
39
39
|
this.#handlers.push(handler);
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -52,20 +52,26 @@ class $DynamicFileLoader {
|
|
|
52
52
|
|
|
53
53
|
await this.#loader.init?.();
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
const handle = (err: Error): void => {
|
|
56
56
|
if (err && (err.message ?? '').includes('Cannot find module')) { // Handle module reloading
|
|
57
57
|
console.error('Cannot find module', { error: err });
|
|
58
|
-
|
|
58
|
+
} else {
|
|
59
|
+
throw err;
|
|
59
60
|
}
|
|
60
|
-
}
|
|
61
|
+
};
|
|
61
62
|
|
|
63
|
+
process
|
|
64
|
+
.on('unhandledRejection', handle)
|
|
65
|
+
.on('uncaughtException', handle);
|
|
62
66
|
|
|
63
67
|
// Fire off, and let it run in the bg. Restart on exit
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
(async (): Promise<void> => {
|
|
69
|
+
for await (const ev of watchCompiler<FullWatchEvent>({ restartOnExit: true })) {
|
|
70
|
+
if (ev.file && RuntimeIndex.hasModule(ev.module) && VALID_FILE_TYPES.has(ManifestModuleUtil.getFileType(ev.file))) {
|
|
71
|
+
await this.dispatch(ev);
|
|
72
|
+
}
|
|
67
73
|
}
|
|
68
|
-
}
|
|
74
|
+
})();
|
|
69
75
|
}
|
|
70
76
|
}
|
|
71
77
|
|
package/src/proxy.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2
|
+
type ConcreteClass<T = any> = new (...args: any[]) => T;
|
|
3
|
+
const ProxyTargetⲐ = Symbol.for('@travetto/base:proxy-target');
|
|
4
|
+
|
|
5
|
+
const AsyncGeneratorFunction = Object.getPrototypeOf(async function* () { });
|
|
6
|
+
const GeneratorFunction = Object.getPrototypeOf(function* () { });
|
|
7
|
+
const AsyncFunction = Object.getPrototypeOf(async function () { });
|
|
8
|
+
|
|
9
|
+
function isFunction(o: unknown): o is Function {
|
|
10
|
+
const proto = o && Object.getPrototypeOf(o);
|
|
11
|
+
return proto && (proto === Function.prototype || proto === AsyncFunction || proto === AsyncGeneratorFunction || proto === GeneratorFunction);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Handler for for proxying modules while watching
|
|
16
|
+
*/
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
export class RetargettingHandler<T> implements ProxyHandler<any> {
|
|
19
|
+
constructor(public target: T) { }
|
|
20
|
+
|
|
21
|
+
isExtensible(target: T): boolean {
|
|
22
|
+
return !Object.isFrozen(this.target);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
getOwnPropertyDescriptor(target: T, property: PropertyKey): PropertyDescriptor | undefined {
|
|
26
|
+
if (property === '__esModule') {
|
|
27
|
+
return undefined;
|
|
28
|
+
} else {
|
|
29
|
+
return Object.getOwnPropertyDescriptor(this.target, property);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
preventExtensions(target: T): boolean {
|
|
34
|
+
return !!Object.preventExtensions(this.target);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
apply(target: T, thisArg: T, argArray?: unknown[]): unknown {
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
39
|
+
return (this.target as unknown as Function).apply(this.target, argArray);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
construct(target: T, argArray: unknown[], newTarget?: unknown): object {
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
44
|
+
return new (this.target as unknown as ConcreteClass)(...argArray);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
setPrototypeOf(target: T, v: unknown): boolean {
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
49
|
+
return Object.setPrototypeOf(this.target, v as Record<string, unknown>);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getPrototypeOf(target: T): object | null {
|
|
53
|
+
return Object.getPrototypeOf(this.target);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
|
+
get(target: T, prop: PropertyKey, receiver: unknown): any {
|
|
58
|
+
if (prop === ProxyTargetⲐ) {
|
|
59
|
+
return this.target;
|
|
60
|
+
}
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
62
|
+
let ret = this.target[prop as keyof T];
|
|
63
|
+
if (isFunction(ret) && !/^class\s/.test(Function.prototype.toString.call(ret))) {
|
|
64
|
+
// Bind class members to class instance instead of proxy propagating
|
|
65
|
+
ret = ret.bind(this.target);
|
|
66
|
+
}
|
|
67
|
+
return ret;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
has(target: T, prop: PropertyKey): boolean {
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
72
|
+
return (this.target as Object).hasOwnProperty(prop);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
set(target: T, prop: PropertyKey, value: unknown): boolean {
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/consistent-type-assertions
|
|
77
|
+
this.target[prop as keyof T] = value as any;
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
ownKeys(target: T): (string | symbol)[] {
|
|
82
|
+
const keys: (string | symbol)[] = [];
|
|
83
|
+
return keys
|
|
84
|
+
.concat(Object.getOwnPropertyNames(this.target))
|
|
85
|
+
.concat(Object.getOwnPropertySymbols(this.target));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
deleteProperty(target: T, p: PropertyKey): boolean {
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
90
|
+
return delete this.target[p as keyof T];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
defineProperty(target: T, p: PropertyKey, attributes: PropertyDescriptor): boolean {
|
|
94
|
+
Object.defineProperty(this.target, p, attributes);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
interface Proxy<T> { }
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Generate Retargetting Proxy
|
|
103
|
+
*/
|
|
104
|
+
export class RetargettingProxy<T> {
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Unwrap proxy
|
|
108
|
+
*/
|
|
109
|
+
static unwrap<U>(el: U): U {
|
|
110
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/consistent-type-assertions
|
|
111
|
+
return (el ? ((el as any)[ProxyTargetⲐ] ?? el) : el) as U;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
#handler: RetargettingHandler<T>;
|
|
115
|
+
#instance: Proxy<T>;
|
|
116
|
+
|
|
117
|
+
constructor(initial: T) {
|
|
118
|
+
this.#handler = new RetargettingHandler(initial);
|
|
119
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
120
|
+
this.#instance = new Proxy({}, this.#handler as ProxyHandler<object>);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
setTarget(next: T): void {
|
|
124
|
+
if (next !== this.#handler.target) {
|
|
125
|
+
this.#handler.target = next;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
getTarget(): T {
|
|
130
|
+
return this.#handler.target;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
get(): T {
|
|
134
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
135
|
+
return this.#instance as T;
|
|
136
|
+
}
|
|
137
|
+
}
|
package/src/registry.ts
CHANGED
package/src/service/metadata.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Class
|
|
1
|
+
import { Class } from '@travetto/base';
|
|
2
2
|
|
|
3
3
|
import { Registry } from '../registry';
|
|
4
4
|
import { ChangeEvent } from '../types';
|
|
@@ -13,7 +13,6 @@ function id(cls: string | Class): string {
|
|
|
13
13
|
export abstract class MetadataRegistry<C extends { class: Class }, M = unknown, F = Function> extends Registry {
|
|
14
14
|
|
|
15
15
|
static id = id;
|
|
16
|
-
|
|
17
16
|
/**
|
|
18
17
|
* Classes pending removal
|
|
19
18
|
*/
|
|
@@ -142,7 +141,7 @@ export abstract class MetadataRegistry<C extends { class: Class }, M = unknown,
|
|
|
142
141
|
*/
|
|
143
142
|
register(cls: Class, pConfig: Partial<C> = {}): void {
|
|
144
143
|
const conf = this.getOrCreatePending(cls);
|
|
145
|
-
|
|
144
|
+
Object.assign(conf, pConfig);
|
|
146
145
|
}
|
|
147
146
|
|
|
148
147
|
/**
|
|
@@ -150,7 +149,7 @@ export abstract class MetadataRegistry<C extends { class: Class }, M = unknown,
|
|
|
150
149
|
*/
|
|
151
150
|
registerField(cls: Class, field: F, pConfig: Partial<M>): void {
|
|
152
151
|
const conf = this.getOrCreatePendingField(cls, field);
|
|
153
|
-
|
|
152
|
+
Object.assign(conf, pConfig);
|
|
154
153
|
}
|
|
155
154
|
|
|
156
155
|
/**
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import { EventEmitter } from 'events';
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
2
|
|
|
3
|
-
import { FindConfig,
|
|
4
|
-
import { Class,
|
|
3
|
+
import { FindConfig, RuntimeIndex } from '@travetto/manifest';
|
|
4
|
+
import { Class, Env } from '@travetto/base';
|
|
5
5
|
|
|
6
6
|
import { DynamicFileLoader } from '../internal/file-loader';
|
|
7
7
|
import { ChangeSource, ChangeEvent, ChangeHandler } from '../types';
|
|
8
8
|
import { PendingRegister } from '../decorator';
|
|
9
9
|
|
|
10
10
|
const moduleFindConfig: FindConfig = {
|
|
11
|
-
module: (m) =>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
module: (m) => {
|
|
12
|
+
const role = Env.TRV_ROLE.val;
|
|
13
|
+
return m.roles.includes('std') && (
|
|
14
|
+
!Env.production || m.prod ||
|
|
15
|
+
((role === 'doc' || role === 'test') && m.roles.includes(role))
|
|
16
|
+
);
|
|
17
|
+
},
|
|
16
18
|
folder: f => f === 'src' || f === '$index'
|
|
17
19
|
};
|
|
18
20
|
|
|
@@ -36,7 +38,7 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
36
38
|
}
|
|
37
39
|
this.#classes.set(file, new Map());
|
|
38
40
|
for (const cls of classes) {
|
|
39
|
-
const src =
|
|
41
|
+
const src = RuntimeIndex.getFunctionMetadata(cls)!.source;
|
|
40
42
|
this.#classes.get(src)!.set(cls.Ⲑid, cls);
|
|
41
43
|
this.emit({ type: 'added', curr: cls });
|
|
42
44
|
}
|
|
@@ -76,8 +78,8 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
76
78
|
changes += 1;
|
|
77
79
|
this.emit({ type: 'added', curr: next.get(k)! });
|
|
78
80
|
} else {
|
|
79
|
-
const prevMeta =
|
|
80
|
-
const nextMeta =
|
|
81
|
+
const prevMeta = RuntimeIndex.getFunctionMetadataFromClass(prev.get(k));
|
|
82
|
+
const nextMeta = RuntimeIndex.getFunctionMetadataFromClass(next.get(k));
|
|
81
83
|
if (prevMeta?.hash !== nextMeta?.hash) {
|
|
82
84
|
changes += 1;
|
|
83
85
|
this.emit({ type: 'changed', curr: next.get(k)!, prev: prev.get(k) });
|
|
@@ -102,7 +104,7 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
102
104
|
* Initialize
|
|
103
105
|
*/
|
|
104
106
|
async init(): Promise<void> {
|
|
105
|
-
if (
|
|
107
|
+
if (Env.dynamic) {
|
|
106
108
|
DynamicFileLoader.onLoadEvent(ev => {
|
|
107
109
|
for (const [file, classes] of PendingRegister.flush(true)) {
|
|
108
110
|
this.#handleFileChanges(file, classes);
|
|
@@ -115,7 +117,7 @@ export class ClassSource implements ChangeSource<Class> {
|
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
// Ensure everything is loaded
|
|
118
|
-
for (const mod of
|
|
120
|
+
for (const mod of RuntimeIndex.find(moduleFindConfig)) {
|
|
119
121
|
await import(mod.import);
|
|
120
122
|
}
|
|
121
123
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { EventEmitter } from 'events';
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
2
|
|
|
3
3
|
import { Class } from '@travetto/base';
|
|
4
|
-
import {
|
|
4
|
+
import { RuntimeIndex } from '@travetto/manifest';
|
|
5
5
|
|
|
6
6
|
import { ChangeSource, ChangeEvent, ChangeHandler } from '../types';
|
|
7
7
|
|
|
@@ -30,8 +30,8 @@ export class MethodSource implements ChangeSource<[Class, Function]> {
|
|
|
30
30
|
* On a class being emitted, check methods
|
|
31
31
|
*/
|
|
32
32
|
onClassEvent(e: ChangeEvent<Class>): void {
|
|
33
|
-
const next =
|
|
34
|
-
const prev =
|
|
33
|
+
const next = RuntimeIndex.getFunctionMetadataFromClass(e.curr!)?.methods ?? {};
|
|
34
|
+
const prev = RuntimeIndex.getFunctionMetadataFromClass(e.prev!)?.methods ?? {};
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Go through each method, comparing hashes. To see added/removed and changed
|