@fluojs/core 1.0.0-beta.1 → 1.0.0-beta.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.ko.md +13 -0
- package/README.md +13 -0
- package/dist/metadata/class-di.d.ts +1 -1
- package/dist/metadata/class-di.d.ts.map +1 -1
- package/dist/metadata/class-di.js +24 -13
- package/dist/metadata/module.d.ts +2 -2
- package/dist/metadata/module.d.ts.map +1 -1
- package/dist/metadata/module.js +57 -13
- package/dist/metadata/shared.d.ts +24 -2
- package/dist/metadata/shared.d.ts.map +1 -1
- package/dist/metadata/shared.js +85 -7
- package/dist/metadata.d.ts +1 -1
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +1 -1
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -60,6 +60,14 @@ class UserService {
|
|
|
60
60
|
|
|
61
61
|
fluo는 TC39 표준 데코레이터를 사용하므로 `experimentalDecorators: true`나 `emitDecoratorMetadata: true`에 의존하지 않습니다.
|
|
62
62
|
|
|
63
|
+
core 메타데이터는 fluo가 소유한 저장소와 TC39 `Symbol.metadata` 통합 지점을 통해 기록되며, `reflect-metadata`나 컴파일러가 생성하는 design type 메타데이터를 사용하지 않습니다. 런타임이 사용자 정의 표준 데코레이터를 평가하기 전에 `Symbol.metadata` 폴리필을 설치해야 한다면 테스트나 부트스트랩 경계에서 `ensureMetadataSymbol()`을 호출하세요.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { ensureMetadataSymbol } from '@fluojs/core';
|
|
67
|
+
|
|
68
|
+
ensureMetadataSymbol();
|
|
69
|
+
```
|
|
70
|
+
|
|
63
71
|
### 명시적인 의존성 메타데이터
|
|
64
72
|
|
|
65
73
|
`@Inject(...)`는 리플렉션 기반 추론 대신 코드 안에서 의존성 토큰을 직접 드러냅니다. 상속된 constructor 토큰을 명시적으로 비우려면 `@Inject()`를 사용하면 됩니다.
|
|
@@ -75,10 +83,14 @@ class UsesConfigValue {
|
|
|
75
83
|
|
|
76
84
|
여러 토큰을 지정할 때는 `@Inject(A, B)`처럼 variadic 호출을 사용하면 됩니다.
|
|
77
85
|
|
|
86
|
+
마이그레이션 기간 동안 legacy 배열 형식인 `@Inject([A, B])`도 계속 정규화되지만, 새 코드는 constructor 토큰을 표준 데코레이터 사용 방식과 맞추기 위해 variadic 형식을 권장합니다.
|
|
87
|
+
|
|
78
88
|
### 형제 패키지를 위한 공용 메타데이터 헬퍼
|
|
79
89
|
|
|
80
90
|
내부 메타데이터 reader/writer는 `@fluojs/core/internal` 아래에 있으며, `@fluojs/di`, `@fluojs/http`, `@fluojs/runtime` 같은 패키지들이 같은 메타데이터 모델을 공유할 수 있게 합니다.
|
|
81
91
|
|
|
92
|
+
애플리케이션 코드는 공개 데코레이터와 `ensureMetadataSymbol()`을 `@fluojs/core`에서 import해야 합니다. `@fluojs/core/internal` 서브패스는 메타데이터 레코드를 읽거나, 명시적 저장소와 `Symbol.metadata`를 병합하거나, 프레임워크 수준 데코레이터를 만드는 fluo 패키지를 위한 경로입니다. 표준 metadata bag helper는 current/native `Symbol.metadata`와 fallback symbol이 섞인 lookup을 처리합니다. 어느 era에서 온 own metadata든 같은 key에 대해서는 어느 era에서 온 inherited metadata보다 우선하지만, child가 다른 key만 소유한 경우 parent constructor의 inherited key는 계속 보입니다. DI와 모듈 그래프 hot path의 allocation을 줄이기 위해 `getModuleMetadata()`, `getOwnClassDiMetadata()`, `getInheritedClassDiMetadata()`, `getClassDiMetadata()`는 frozen snapshot을 반환하며 write 사이에는 같은 reference를 재사용할 수 있습니다. 이 결과, collection 필드, module provider descriptor wrapper와 middleware route-config wrapper(그 안의 `routes` 배열 포함)는 immutable로 취급해야 합니다. `useValue` payload 객체와 runtime middleware/guard/interceptor instance는 mutable reference로 유지되며 이 snapshot 때문에 freeze되지 않습니다. 다른 metadata reader는 각 reader의 테스트가 stable-reference 재사용을 문서화하지 않는 한 기존 defensive-read 동작을 유지합니다.
|
|
93
|
+
|
|
82
94
|
```ts
|
|
83
95
|
import { getModuleMetadata } from '@fluojs/core/internal';
|
|
84
96
|
|
|
@@ -149,6 +161,7 @@ class Logger {}
|
|
|
149
161
|
|
|
150
162
|
- **데코레이터**: `Module`, `Global`, `Inject`, `Scope`
|
|
151
163
|
- **에러**: `FluoError`, `InvariantError`, `FluoCodeError`
|
|
164
|
+
- **메타데이터 런타임**: `ensureMetadataSymbol`
|
|
152
165
|
- **타입**: `Constructor<T>`, `Token<T>`, `MaybePromise<T>`, `AsyncModuleOptions`
|
|
153
166
|
- **내부 서브패스**: `@fluojs/core/internal`을 통한 메타데이터 헬퍼
|
|
154
167
|
|
package/README.md
CHANGED
|
@@ -62,6 +62,14 @@ class UserService {
|
|
|
62
62
|
|
|
63
63
|
fluo uses TC39 standard decorators. You do not need `experimentalDecorators: true` or `emitDecoratorMetadata: true` to use `@Module`, `@Inject`, `@Global`, or `@Scope`.
|
|
64
64
|
|
|
65
|
+
Core metadata is written through fluo-owned stores and TC39 `Symbol.metadata` integration points, never through `reflect-metadata` or compiler-emitted design types. Call `ensureMetadataSymbol()` at test or bootstrap boundaries when a runtime needs the `Symbol.metadata` polyfill installed before evaluating custom standard decorators.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { ensureMetadataSymbol } from '@fluojs/core';
|
|
69
|
+
|
|
70
|
+
ensureMetadataSymbol();
|
|
71
|
+
```
|
|
72
|
+
|
|
65
73
|
### Explicit dependency metadata
|
|
66
74
|
|
|
67
75
|
`@Inject(...)` keeps dependency wiring visible in code instead of relying on emitted reflection metadata. Call `@Inject()` when you want to record an explicit empty override for inherited constructor tokens.
|
|
@@ -77,10 +85,14 @@ class UsesConfigValue {
|
|
|
77
85
|
|
|
78
86
|
Pass multiple tokens as variadic arguments such as `@Inject(A, B)`.
|
|
79
87
|
|
|
88
|
+
The legacy array form `@Inject([A, B])` remains normalized during the migration window, but new code should prefer the variadic form so constructor tokens stay aligned with standard decorator usage.
|
|
89
|
+
|
|
80
90
|
### Shared metadata helpers for sibling packages
|
|
81
91
|
|
|
82
92
|
Internal readers and writers live under `@fluojs/core/internal`, which is how packages like `@fluojs/di`, `@fluojs/http`, and `@fluojs/runtime` consume the same metadata model.
|
|
83
93
|
|
|
94
|
+
Application code should import public decorators and `ensureMetadataSymbol()` from `@fluojs/core`. The `@fluojs/core/internal` subpath is reserved for fluo packages that need to read metadata records, merge explicit stores with `Symbol.metadata`, or build framework-level decorators. Standard metadata bag helpers handle mixed-era lookups across current/native `Symbol.metadata` and the fallback symbol: own metadata from either era overrides inherited metadata from either era for the same key, while inherited keys from parent constructors remain visible when the child owns a different key. To reduce DI and module-graph hot-path allocations, `getModuleMetadata()`, `getOwnClassDiMetadata()`, `getInheritedClassDiMetadata()`, and `getClassDiMetadata()` return frozen snapshots and may reuse the same reference between writes. Treat those results, their collection fields, and module provider descriptor wrappers, and middleware route-config wrappers (including their `routes` arrays) as immutable. `useValue` payload objects and runtime middleware/guard/interceptor instances remain mutable references and are not frozen by these snapshots. Other metadata readers keep their existing defensive-read behavior unless their own tests document stable-reference reuse.
|
|
95
|
+
|
|
84
96
|
```ts
|
|
85
97
|
import { getModuleMetadata } from '@fluojs/core/internal';
|
|
86
98
|
|
|
@@ -151,6 +163,7 @@ Standard decorators cannot automatically infer types for abstract classes or int
|
|
|
151
163
|
|
|
152
164
|
- **Decorators**: `Module`, `Global`, `Inject`, `Scope`
|
|
153
165
|
- **Errors**: `FluoError`, `InvariantError`, `FluoCodeError`
|
|
166
|
+
- **Metadata runtime**: `ensureMetadataSymbol`
|
|
154
167
|
- **Types**: `Constructor<T>`, `Token<T>`, `MaybePromise<T>`, `AsyncModuleOptions`
|
|
155
168
|
- **Internal subpath**: metadata helpers via `@fluojs/core/internal`
|
|
156
169
|
|
|
@@ -10,7 +10,7 @@ export declare function defineClassDiMetadata(target: Function, metadata: ClassD
|
|
|
10
10
|
* Reads only the DI metadata defined directly on a class.
|
|
11
11
|
*
|
|
12
12
|
* @param target Class being inspected.
|
|
13
|
-
* @returns A
|
|
13
|
+
* @returns A frozen class DI metadata snapshot, or `undefined` when absent.
|
|
14
14
|
*/
|
|
15
15
|
export declare function getOwnClassDiMetadata(target: Function): ClassDiMetadata | undefined;
|
|
16
16
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"class-di.d.ts","sourceRoot":"","sources":["../../src/metadata/class-di.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"class-di.d.ts","sourceRoot":"","sources":["../../src/metadata/class-di.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA2BlD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI,CAQvF;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,GAAG,eAAe,GAAG,SAAS,CAEnF;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,QAAQ,GAAG,eAAe,GAAG,SAAS,CA4BzF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,GAAG,eAAe,GAAG,SAAS,CAEhF"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
const classDiMetadataStore = new WeakMap();
|
|
2
|
+
const inheritedClassDiMetadataCache = new WeakMap();
|
|
3
|
+
let classDiMetadataVersion = 0;
|
|
4
|
+
function freezeClassDiMetadata(metadata) {
|
|
5
|
+
return Object.freeze({
|
|
6
|
+
inject: metadata.inject ? Object.freeze([...metadata.inject]) : undefined,
|
|
6
7
|
scope: metadata.scope
|
|
7
|
-
};
|
|
8
|
+
});
|
|
8
9
|
}
|
|
9
10
|
function getClassMetadataLineage(target) {
|
|
10
11
|
const lineage = [];
|
|
@@ -24,20 +25,22 @@ function getClassMetadataLineage(target) {
|
|
|
24
25
|
* @param metadata Partial or complete DI metadata payload.
|
|
25
26
|
*/
|
|
26
27
|
export function defineClassDiMetadata(target, metadata) {
|
|
27
|
-
|
|
28
|
+
const existing = classDiMetadataStore.get(target);
|
|
29
|
+
classDiMetadataStore.set(target, freezeClassDiMetadata({
|
|
28
30
|
inject: metadata.inject !== undefined ? metadata.inject : existing?.inject,
|
|
29
31
|
scope: metadata.scope ?? existing?.scope
|
|
30
32
|
}));
|
|
33
|
+
classDiMetadataVersion += 1;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
/**
|
|
34
37
|
* Reads only the DI metadata defined directly on a class.
|
|
35
38
|
*
|
|
36
39
|
* @param target Class being inspected.
|
|
37
|
-
* @returns A
|
|
40
|
+
* @returns A frozen class DI metadata snapshot, or `undefined` when absent.
|
|
38
41
|
*/
|
|
39
42
|
export function getOwnClassDiMetadata(target) {
|
|
40
|
-
return classDiMetadataStore.
|
|
43
|
+
return classDiMetadataStore.get(target);
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
/**
|
|
@@ -47,18 +50,26 @@ export function getOwnClassDiMetadata(target) {
|
|
|
47
50
|
* @returns The effective inherited DI metadata, or `undefined` when no lineage metadata exists.
|
|
48
51
|
*/
|
|
49
52
|
export function getInheritedClassDiMetadata(target) {
|
|
53
|
+
const cached = inheritedClassDiMetadataCache.get(target);
|
|
54
|
+
if (cached?.version === classDiMetadataVersion) {
|
|
55
|
+
return cached.metadata ?? undefined;
|
|
56
|
+
}
|
|
50
57
|
let effective;
|
|
51
58
|
for (const constructor of getClassMetadataLineage(target)) {
|
|
52
|
-
const metadata = classDiMetadataStore.
|
|
59
|
+
const metadata = classDiMetadataStore.get(constructor);
|
|
53
60
|
if (!metadata) {
|
|
54
61
|
continue;
|
|
55
62
|
}
|
|
56
|
-
effective = {
|
|
63
|
+
effective = freezeClassDiMetadata({
|
|
57
64
|
inject: metadata.inject ?? effective?.inject,
|
|
58
65
|
scope: metadata.scope ?? effective?.scope
|
|
59
|
-
};
|
|
66
|
+
});
|
|
60
67
|
}
|
|
61
|
-
|
|
68
|
+
inheritedClassDiMetadataCache.set(target, {
|
|
69
|
+
metadata: effective ?? null,
|
|
70
|
+
version: classDiMetadataVersion
|
|
71
|
+
});
|
|
72
|
+
return effective;
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
/**
|
|
@@ -7,10 +7,10 @@ import type { ModuleMetadata } from './types.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export declare function defineModuleMetadata(target: Function, metadata: ModuleMetadata): void;
|
|
9
9
|
/**
|
|
10
|
-
* Reads
|
|
10
|
+
* Reads frozen module metadata for the provided module class.
|
|
11
11
|
*
|
|
12
12
|
* @param target Module class being inspected.
|
|
13
|
-
* @returns A
|
|
13
|
+
* @returns A frozen module metadata snapshot, or `undefined` when none was defined.
|
|
14
14
|
*/
|
|
15
15
|
export declare function getModuleMetadata(target: Function): ModuleMetadata | undefined;
|
|
16
16
|
//# sourceMappingURL=module.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../src/metadata/module.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../src/metadata/module.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AA8FjD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI,CAWrF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,GAAG,cAAc,GAAG,SAAS,CAE9E"}
|
package/dist/metadata/module.js
CHANGED
|
@@ -1,33 +1,76 @@
|
|
|
1
1
|
import { cloneCollection, cloneMutableValue } from './shared.js';
|
|
2
|
-
|
|
3
|
-
const moduleMetadataStore = createClonedWeakMapStore(cloneModuleMetadata);
|
|
2
|
+
const moduleMetadataStore = new WeakMap();
|
|
4
3
|
function isValueProvider(provider) {
|
|
5
4
|
return typeof provider === 'object' && provider !== null && 'useValue' in provider;
|
|
6
5
|
}
|
|
6
|
+
function cloneProviderDescriptorFields(provider) {
|
|
7
|
+
const clonedProvider = {
|
|
8
|
+
...provider
|
|
9
|
+
};
|
|
10
|
+
if (Array.isArray(clonedProvider.inject)) {
|
|
11
|
+
clonedProvider.inject = [...clonedProvider.inject];
|
|
12
|
+
}
|
|
13
|
+
return clonedProvider;
|
|
14
|
+
}
|
|
15
|
+
function freezeProviderDescriptor(provider) {
|
|
16
|
+
const inject = provider.inject;
|
|
17
|
+
if (Array.isArray(inject)) {
|
|
18
|
+
Object.freeze(inject);
|
|
19
|
+
}
|
|
20
|
+
return Object.freeze(provider);
|
|
21
|
+
}
|
|
7
22
|
function cloneProvider(provider) {
|
|
8
23
|
if (isValueProvider(provider)) {
|
|
9
|
-
// Shallow-copy
|
|
24
|
+
// Shallow-copy descriptor fields but preserve the useValue reference.
|
|
10
25
|
// Deep-cloning useValue would sever object identity for externally supplied
|
|
11
26
|
// instances (e.g. transport adapters) that callers hold references to.
|
|
12
|
-
return
|
|
13
|
-
...provider
|
|
14
|
-
};
|
|
27
|
+
return freezeProviderDescriptor(cloneProviderDescriptorFields(provider));
|
|
15
28
|
}
|
|
16
|
-
|
|
29
|
+
const clonedProvider = cloneMutableValue(provider);
|
|
30
|
+
return typeof clonedProvider === 'object' && clonedProvider !== null ? freezeProviderDescriptor(clonedProvider) : clonedProvider;
|
|
17
31
|
}
|
|
18
32
|
function cloneProviders(providers) {
|
|
19
33
|
return providers ? providers.map(cloneProvider) : undefined;
|
|
20
34
|
}
|
|
35
|
+
function isMiddlewareRouteConfig(middleware) {
|
|
36
|
+
return typeof middleware === 'object' && middleware !== null && 'middleware' in middleware && 'routes' in middleware;
|
|
37
|
+
}
|
|
38
|
+
function cloneMiddlewareRouteConfig(middleware) {
|
|
39
|
+
const routes = Array.isArray(middleware.routes) ? Object.freeze([...middleware.routes]) : middleware.routes;
|
|
40
|
+
return Object.freeze({
|
|
41
|
+
...middleware,
|
|
42
|
+
routes
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function cloneMiddleware(middleware) {
|
|
46
|
+
return isMiddlewareRouteConfig(middleware) ? cloneMiddlewareRouteConfig(middleware) : middleware;
|
|
47
|
+
}
|
|
48
|
+
function cloneMiddlewareCollection(middleware) {
|
|
49
|
+
return middleware ? middleware.map(cloneMiddleware) : undefined;
|
|
50
|
+
}
|
|
21
51
|
function cloneModuleMetadata(metadata) {
|
|
22
52
|
return {
|
|
23
53
|
controllers: cloneCollection(metadata.controllers),
|
|
24
54
|
exports: cloneCollection(metadata.exports),
|
|
25
55
|
global: metadata.global,
|
|
26
56
|
imports: cloneCollection(metadata.imports),
|
|
27
|
-
middleware:
|
|
57
|
+
middleware: cloneMiddlewareCollection(metadata.middleware),
|
|
28
58
|
providers: cloneProviders(metadata.providers)
|
|
29
59
|
};
|
|
30
60
|
}
|
|
61
|
+
function freezeCollection(collection) {
|
|
62
|
+
return collection ? Object.freeze(collection) : undefined;
|
|
63
|
+
}
|
|
64
|
+
function freezeModuleMetadata(metadata) {
|
|
65
|
+
return Object.freeze({
|
|
66
|
+
controllers: freezeCollection(metadata.controllers),
|
|
67
|
+
exports: freezeCollection(metadata.exports),
|
|
68
|
+
global: metadata.global,
|
|
69
|
+
imports: freezeCollection(metadata.imports),
|
|
70
|
+
middleware: freezeCollection(metadata.middleware),
|
|
71
|
+
providers: freezeCollection(metadata.providers)
|
|
72
|
+
});
|
|
73
|
+
}
|
|
31
74
|
|
|
32
75
|
/**
|
|
33
76
|
* Defines module metadata while preserving previously written fields for partial decorator passes.
|
|
@@ -36,22 +79,23 @@ function cloneModuleMetadata(metadata) {
|
|
|
36
79
|
* @param metadata Partial or complete module metadata payload.
|
|
37
80
|
*/
|
|
38
81
|
export function defineModuleMetadata(target, metadata) {
|
|
39
|
-
|
|
82
|
+
const existing = moduleMetadataStore.get(target);
|
|
83
|
+
moduleMetadataStore.set(target, freezeModuleMetadata(cloneModuleMetadata({
|
|
40
84
|
controllers: metadata.controllers ?? existing?.controllers,
|
|
41
85
|
exports: metadata.exports ?? existing?.exports,
|
|
42
86
|
global: metadata.global !== undefined ? metadata.global : existing?.global,
|
|
43
87
|
imports: metadata.imports ?? existing?.imports,
|
|
44
88
|
middleware: metadata.middleware ?? existing?.middleware,
|
|
45
89
|
providers: metadata.providers ?? existing?.providers
|
|
46
|
-
}));
|
|
90
|
+
})));
|
|
47
91
|
}
|
|
48
92
|
|
|
49
93
|
/**
|
|
50
|
-
* Reads
|
|
94
|
+
* Reads frozen module metadata for the provided module class.
|
|
51
95
|
*
|
|
52
96
|
* @param target Module class being inspected.
|
|
53
|
-
* @returns A
|
|
97
|
+
* @returns A frozen module metadata snapshot, or `undefined` when none was defined.
|
|
54
98
|
*/
|
|
55
99
|
export function getModuleMetadata(target) {
|
|
56
|
-
return moduleMetadataStore.
|
|
100
|
+
return moduleMetadataStore.get(target);
|
|
57
101
|
}
|
|
@@ -71,14 +71,36 @@ export declare function getOrCreatePropertyMap<T>(store: WeakMap<object, Map<Met
|
|
|
71
71
|
*/
|
|
72
72
|
export declare function mergeUnique<T>(existing: readonly T[] | undefined, values: readonly T[] | undefined): T[] | undefined;
|
|
73
73
|
/**
|
|
74
|
-
* Reads the standard metadata bag
|
|
74
|
+
* Reads the standard metadata bag owned directly by a constructor.
|
|
75
|
+
*
|
|
76
|
+
* Own current/native metadata wins over own fallback-era metadata. When the
|
|
77
|
+
* constructor owns neither era, this helper returns `undefined`; it does not
|
|
78
|
+
* inspect inherited constructors.
|
|
79
|
+
*
|
|
80
|
+
* @param constructor Constructor whose own metadata bag should be inspected.
|
|
81
|
+
* @returns The own metadata bag when present, otherwise `undefined`.
|
|
82
|
+
*/
|
|
83
|
+
export declare function getOwnStandardConstructorMetadataBag(constructor: Function): StandardMetadataBag | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* Reads the effective standard metadata bag for a target.
|
|
86
|
+
*
|
|
87
|
+
* Lookup prefers the target's own current/native metadata bag, then the target's
|
|
88
|
+
* own fallback-era bag, then inherited bags using the same per-object era order.
|
|
89
|
+
* When an own bag from either era exists alongside inherited metadata from
|
|
90
|
+
* either era, the returned bag preserves own-key precedence while allowing
|
|
91
|
+
* property lookup to fall through to inherited records.
|
|
75
92
|
*
|
|
76
93
|
* @param target Target object that may own standard metadata.
|
|
77
94
|
* @returns The metadata bag when present, otherwise `undefined`.
|
|
78
95
|
*/
|
|
79
96
|
export declare function getStandardMetadataBag(target: object): StandardMetadataBag | undefined;
|
|
80
97
|
/**
|
|
81
|
-
* Reads the standard metadata bag stored on a target's constructor.
|
|
98
|
+
* Reads the effective standard metadata bag stored on a target's constructor.
|
|
99
|
+
*
|
|
100
|
+
* Constructor lookup delegates to {@link getStandardMetadataBag}, so it uses the
|
|
101
|
+
* constructor's own current/native bag, own fallback-era bag, then inherited
|
|
102
|
+
* constructor bags. Own current/native records retain precedence while inherited
|
|
103
|
+
* records remain visible through property lookup.
|
|
82
104
|
*
|
|
83
105
|
* @param target Instance or prototype whose constructor metadata should be inspected.
|
|
84
106
|
* @returns The constructor metadata bag when present, otherwise `undefined`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/metadata/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGvD;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/metadata/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGvD;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AAK/D;;GAEG;AACH,eAAO,IAAI,cAAc,QAAwD,CAAC;AAElF;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAY7C;AA+ED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAMhD;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;;CAOvB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;;;CASf,CAAC;AAEX;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,GAAG,SAAS,GAAG,CAAC,EAAE,GAAG,SAAS,CAExF;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC,EACnD,MAAM,EAAE,MAAM,GACb,GAAG,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAS7B;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,GAAG,SAAS,GAAG,CAAC,EAAE,GAAG,SAAS,CAgBpH;AAGD;;;;;;;;;GASG;AACH,wBAAgB,oCAAoC,CAAC,WAAW,EAAE,QAAQ,GAAG,mBAAmB,GAAG,SAAS,CAE3G;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAiBtF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAIjG;AAED;;;;;;GAMG;AACH,wBAAgB,oCAAoC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAElG;AAED;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,mBAAmB,EAAE,CAAC,CAAC,GAAG,SAAS,CAEzH;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,SAAS,EAC1D,MAAM,EAAE,WAAW,CAAC,mBAAmB,EAAE,OAAO,CAAC,GAAG,SAAS,EAC7D,QAAQ,EAAE,WAAW,CAAC,mBAAmB,EAAE,SAAS,CAAC,GAAG,SAAS,GAChE,mBAAmB,EAAE,CAkBvB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,mBAAmB,EAAE,CAAC,EAAE,CAAC,CAAC,EACrD,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,mBAAmB,EAChC,KAAK,EAAE,CAAC,GACP,IAAI,CAUN;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CASrG"}
|
package/dist/metadata/shared.js
CHANGED
|
@@ -5,10 +5,12 @@ import { fallbackClone } from '../utils.js';
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const symbolWithMetadata = Symbol;
|
|
8
|
+
const fallbackMetadataSymbol = Symbol.for('fluo.symbol.metadata');
|
|
9
|
+
|
|
8
10
|
/**
|
|
9
11
|
* Active symbol key used to read and write standard metadata bags.
|
|
10
12
|
*/
|
|
11
|
-
export let metadataSymbol = symbolWithMetadata.metadata ??
|
|
13
|
+
export let metadataSymbol = symbolWithMetadata.metadata ?? fallbackMetadataSymbol;
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* Ensures `Symbol.metadata` exists and returns the symbol used by Fluo metadata helpers.
|
|
@@ -27,6 +29,50 @@ export function ensureMetadataSymbol() {
|
|
|
27
29
|
return metadataSymbol;
|
|
28
30
|
}
|
|
29
31
|
void ensureMetadataSymbol();
|
|
32
|
+
function getActiveMetadataSymbol() {
|
|
33
|
+
const nativeMetadataSymbol = symbolWithMetadata.metadata;
|
|
34
|
+
if (nativeMetadataSymbol && nativeMetadataSymbol !== metadataSymbol) {
|
|
35
|
+
metadataSymbol = nativeMetadataSymbol;
|
|
36
|
+
}
|
|
37
|
+
return metadataSymbol;
|
|
38
|
+
}
|
|
39
|
+
function getOwnStandardMetadataBagFromSymbol(target, symbol) {
|
|
40
|
+
if (!Object.prototype.hasOwnProperty.call(target, symbol)) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
const metadata = Reflect.get(target, symbol);
|
|
44
|
+
if (typeof metadata !== 'object' || metadata === null) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
return metadata;
|
|
48
|
+
}
|
|
49
|
+
function getOwnStandardMetadataBagForEra(target, activeMetadataSymbol) {
|
|
50
|
+
const activeMetadata = getOwnStandardMetadataBagFromSymbol(target, activeMetadataSymbol);
|
|
51
|
+
if (activeMetadata) {
|
|
52
|
+
return activeMetadata;
|
|
53
|
+
}
|
|
54
|
+
if (activeMetadataSymbol !== fallbackMetadataSymbol) {
|
|
55
|
+
return getOwnStandardMetadataBagFromSymbol(target, fallbackMetadataSymbol);
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
function getInheritedStandardMetadataBag(target, activeMetadataSymbol) {
|
|
60
|
+
let prototype = Object.getPrototypeOf(target);
|
|
61
|
+
while (prototype) {
|
|
62
|
+
const inheritedMetadata = getOwnStandardMetadataBagForEra(prototype, activeMetadataSymbol);
|
|
63
|
+
if (inheritedMetadata) {
|
|
64
|
+
return inheritedMetadata;
|
|
65
|
+
}
|
|
66
|
+
prototype = Object.getPrototypeOf(prototype);
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
function overlayStandardMetadataBag(ownMetadata, inheritedMetadata) {
|
|
71
|
+
if (!inheritedMetadata) {
|
|
72
|
+
return ownMetadata;
|
|
73
|
+
}
|
|
74
|
+
return Object.create(inheritedMetadata, Object.getOwnPropertyDescriptors(ownMetadata));
|
|
75
|
+
}
|
|
30
76
|
function isPlainObject(value) {
|
|
31
77
|
if (typeof value !== 'object' || value === null) {
|
|
32
78
|
return false;
|
|
@@ -126,21 +172,53 @@ export function mergeUnique(existing, values) {
|
|
|
126
172
|
}
|
|
127
173
|
|
|
128
174
|
/**
|
|
129
|
-
* Reads the standard metadata bag
|
|
175
|
+
* Reads the standard metadata bag owned directly by a constructor.
|
|
176
|
+
*
|
|
177
|
+
* Own current/native metadata wins over own fallback-era metadata. When the
|
|
178
|
+
* constructor owns neither era, this helper returns `undefined`; it does not
|
|
179
|
+
* inspect inherited constructors.
|
|
180
|
+
*
|
|
181
|
+
* @param constructor Constructor whose own metadata bag should be inspected.
|
|
182
|
+
* @returns The own metadata bag when present, otherwise `undefined`.
|
|
183
|
+
*/
|
|
184
|
+
export function getOwnStandardConstructorMetadataBag(constructor) {
|
|
185
|
+
return getOwnStandardMetadataBagForEra(constructor, getActiveMetadataSymbol());
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Reads the effective standard metadata bag for a target.
|
|
190
|
+
*
|
|
191
|
+
* Lookup prefers the target's own current/native metadata bag, then the target's
|
|
192
|
+
* own fallback-era bag, then inherited bags using the same per-object era order.
|
|
193
|
+
* When an own bag from either era exists alongside inherited metadata from
|
|
194
|
+
* either era, the returned bag preserves own-key precedence while allowing
|
|
195
|
+
* property lookup to fall through to inherited records.
|
|
130
196
|
*
|
|
131
197
|
* @param target Target object that may own standard metadata.
|
|
132
198
|
* @returns The metadata bag when present, otherwise `undefined`.
|
|
133
199
|
*/
|
|
134
200
|
export function getStandardMetadataBag(target) {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
201
|
+
const activeMetadataSymbol = getActiveMetadataSymbol();
|
|
202
|
+
const ownActiveMetadata = getOwnStandardMetadataBagFromSymbol(target, activeMetadataSymbol);
|
|
203
|
+
if (ownActiveMetadata) {
|
|
204
|
+
return overlayStandardMetadataBag(ownActiveMetadata, getInheritedStandardMetadataBag(target, activeMetadataSymbol));
|
|
138
205
|
}
|
|
139
|
-
|
|
206
|
+
if (activeMetadataSymbol !== fallbackMetadataSymbol) {
|
|
207
|
+
const ownFallbackMetadata = getOwnStandardMetadataBagFromSymbol(target, fallbackMetadataSymbol);
|
|
208
|
+
if (ownFallbackMetadata) {
|
|
209
|
+
return overlayStandardMetadataBag(ownFallbackMetadata, getInheritedStandardMetadataBag(target, activeMetadataSymbol));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return getInheritedStandardMetadataBag(target, activeMetadataSymbol);
|
|
140
213
|
}
|
|
141
214
|
|
|
142
215
|
/**
|
|
143
|
-
* Reads the standard metadata bag stored on a target's constructor.
|
|
216
|
+
* Reads the effective standard metadata bag stored on a target's constructor.
|
|
217
|
+
*
|
|
218
|
+
* Constructor lookup delegates to {@link getStandardMetadataBag}, so it uses the
|
|
219
|
+
* constructor's own current/native bag, own fallback-era bag, then inherited
|
|
220
|
+
* constructor bags. Own current/native records retain precedence while inherited
|
|
221
|
+
* records remain visible through property lookup.
|
|
144
222
|
*
|
|
145
223
|
* @param target Instance or prototype whose constructor metadata should be inspected.
|
|
146
224
|
* @returns The constructor metadata bag when present, otherwise `undefined`.
|
package/dist/metadata.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { getClassDiMetadata, getInheritedClassDiMetadata, getOwnClassDiMetadata,
|
|
|
2
2
|
export { defineControllerMetadata, defineRouteMetadata, getControllerMetadata, getRouteMetadata } from './metadata/controller-route.js';
|
|
3
3
|
export { defineInjectionMetadata, getInjectionSchema } from './metadata/injection.js';
|
|
4
4
|
export { defineModuleMetadata, getModuleMetadata } from './metadata/module.js';
|
|
5
|
-
export { ensureMetadataSymbol, metadataKeys, metadataSymbol } from './metadata/shared.js';
|
|
5
|
+
export { ensureMetadataSymbol, getOwnStandardConstructorMetadataBag, getStandardConstructorMetadataBag, getStandardMetadataBag, metadataKeys, metadataSymbol, } from './metadata/shared.js';
|
|
6
6
|
export { ensureSymbolMetadataPolyfill } from './metadata/symbol-metadata-polyfill.js';
|
|
7
7
|
export { appendClassValidationRule, appendDtoFieldValidationRule, defineDtoFieldBindingMetadata, getClassValidationRules, getDtoBindingSchema, getDtoFieldBindingMetadata, getDtoFieldValidationRules, getDtoValidationSchema, } from './metadata/validation.js';
|
|
8
8
|
export type { ClassDiMetadata, ClassValidationRule, ConditionalFieldValidator, ControllerMetadata, CustomClassValidator, CustomFieldValidationContext, CustomFieldValidator, CustomValidationDecoratorOptions, DtoBindingSchemaEntry, DtoFieldBindingMetadata, DtoFieldValidationRule, DtoValidationSchemaEntry, InjectionMetadata, InjectionSchemaEntry, MetadataCollection, ModuleMetadata, RouteHeader, RouteMetadata, RouteRedirect, ValidationDecoratorOptions, ValidationIssueMetadata, ValidationRuleResult, } from './metadata/types.js';
|
package/dist/metadata.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AACvI,OAAO,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACxI,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AACvI,OAAO,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACxI,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EACL,oBAAoB,EACpB,oCAAoC,EACpC,iCAAiC,EACjC,sBAAsB,EACtB,YAAY,EACZ,cAAc,GACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,4BAA4B,EAAE,MAAM,wCAAwC,CAAC;AACtF,OAAO,EACL,yBAAyB,EACzB,4BAA4B,EAC5B,6BAA6B,EAC7B,uBAAuB,EACvB,mBAAmB,EACnB,0BAA0B,EAC1B,0BAA0B,EAC1B,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EACzB,kBAAkB,EAClB,oBAAoB,EACpB,4BAA4B,EAC5B,oBAAoB,EACpB,gCAAgC,EAChC,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,EACtB,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,aAAa,EACb,aAAa,EACb,0BAA0B,EAC1B,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC"}
|
package/dist/metadata.js
CHANGED
|
@@ -2,6 +2,6 @@ export { getClassDiMetadata, getInheritedClassDiMetadata, getOwnClassDiMetadata,
|
|
|
2
2
|
export { defineControllerMetadata, defineRouteMetadata, getControllerMetadata, getRouteMetadata } from './metadata/controller-route.js';
|
|
3
3
|
export { defineInjectionMetadata, getInjectionSchema } from './metadata/injection.js';
|
|
4
4
|
export { defineModuleMetadata, getModuleMetadata } from './metadata/module.js';
|
|
5
|
-
export { ensureMetadataSymbol, metadataKeys, metadataSymbol } from './metadata/shared.js';
|
|
5
|
+
export { ensureMetadataSymbol, getOwnStandardConstructorMetadataBag, getStandardConstructorMetadataBag, getStandardMetadataBag, metadataKeys, metadataSymbol } from './metadata/shared.js';
|
|
6
6
|
export { ensureSymbolMetadataPolyfill } from './metadata/symbol-metadata-polyfill.js';
|
|
7
7
|
export { appendClassValidationRule, appendDtoFieldValidationRule, defineDtoFieldBindingMetadata, getClassValidationRules, getDtoBindingSchema, getDtoFieldBindingMetadata, getDtoFieldValidationRules, getDtoValidationSchema } from './metadata/validation.js';
|