Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
cf7eb49
feat(core): add module plugin core mechanism
gxkl Jul 4, 2026
17e64b1
feat(standalone): two-phase StandaloneApp with module plugin support
gxkl Jul 4, 2026
fa6cb8e
feat(tegg-plugin): instantiate InnerObjectLoadUnit in app mode
gxkl Jul 4, 2026
bae48ee
feat(tegg): convert built-in framework hooks to module plugins
gxkl Jul 4, 2026
77bf847
docs(wiki): record tegg module plugin architecture
gxkl Jul 4, 2026
5dc8d65
refactor(tegg): address module-plugin review - dedupe hook, untangle …
gxkl Jul 5, 2026
76094c0
refactor(tegg): built-in framework hooks load through the module scan
gxkl Jul 5, 2026
d66f4b0
refactor(tegg): decentralize module plugins - enabling the plugin IS …
gxkl Jul 5, 2026
888b427
refactor(tegg): the aop PLUGIN is the aop module; provided objects jo…
gxkl Jul 5, 2026
a3db4cf
refactor(runtime): provided inner objects are ordinary protos end to end
gxkl Jul 5, 2026
1e8901d
chore(aop-runtime): drop leftover InnerObjects re-export
gxkl Jul 5, 2026
93b3abb
refactor(tegg): discovery stays deps/module.json-declared; drop enabl…
gxkl Jul 5, 2026
013ef5c
refactor(runtime): name the provided-instance factory for what it is
gxkl Jul 5, 2026
bffe51b
docs(aop): explain why AopContextHook stays imperatively registered
gxkl Jul 5, 2026
234961d
docs(tegg): explain why inner objects use provided instances, not egg…
gxkl Jul 5, 2026
8396f02
docs(tegg): state why ConfigSourceLoadUnitHook is a host built-in
gxkl Jul 5, 2026
97de01b
refactor(tegg): ConfigSourceLoadUnitHook lives in @eggjs/tegg-config,…
gxkl Jul 5, 2026
e00f50d
fix(tegg): gate optional modules' hooks; fix the enabled-promotion match
gxkl Jul 5, 2026
16784ec
revert(tegg-config): framework discovery reads package.json only
gxkl Jul 5, 2026
ed70cd4
refactor(standalone): defer module scan out of the StandaloneApp cons…
gxkl Jul 5, 2026
172072d
refactor(standalone): drop deprecated StandaloneAppOptions.innerObjects
gxkl Jul 5, 2026
0c21e5b
refactor(standalone): keep innerObjects assembly in the constructor, …
gxkl Jul 5, 2026
368d202
refactor(standalone): make runtimeConfig a placeholder filled at init
gxkl Jul 5, 2026
943bcb3
feat(standalone): accept a logger via StandaloneAppOptions
gxkl Jul 5, 2026
669506f
refactor(standalone): make StandaloneApp.init idempotent
gxkl Jul 5, 2026
a797445
fix(standalone): keep host moduleConfig handler override semantics
gxkl Jul 5, 2026
34c8619
refactor(standalone): build framework base inner objects at the point…
gxkl Jul 5, 2026
8752841
refactor(standalone): align StandaloneApp API with tegg#325
gxkl Jul 5, 2026
f24677e
refactor(standalone): discover builtin framework plugins via own pack…
gxkl Jul 5, 2026
df3af31
refactor(standalone): move dal manager cleanup into the dal module it…
gxkl Jul 5, 2026
8f2542b
feat(dal): declare the mysqlDataSourceManager injection surface in th…
gxkl Jul 5, 2026
54abd7f
fix(dal): declare manager cleanup via @LifecycleDestroy
gxkl Jul 5, 2026
2d31e55
refactor(dal): type MysqlDataSourceManagerObject via declaration merging
gxkl Jul 5, 2026
61eb83e
fix(standalone): allowlist scan-declaration deps for unplugin-unused
gxkl Jul 5, 2026
dd80199
fix(core-decorator): explicit return types for isolatedDeclarations
gxkl Jul 5, 2026
ae83327
fix(config): default the framework module scan to egg
gxkl Jul 5, 2026
d632166
refactor(config): resolve the framework dir with getFrameworkPath
gxkl Jul 5, 2026
9a5cc21
fix(config): tolerate non-materialized module dirs from bundle manifests
gxkl Jul 5, 2026
9c05389
fix(tegg): JSON-safe descriptor dump and bundle-tolerant module confi…
gxkl Jul 5, 2026
8f4bad1
test(tegg): explicit boot-hook timeouts for slow Windows runners
gxkl Jul 5, 2026
79594a6
test(tegg): align boot-hook timeouts with the root config values
gxkl Jul 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions tegg/core/aop-decorator/src/CrosscutAdviceFactory.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import assert from 'node:assert';

import { InnerObjectProto } from '@eggjs/core-decorator';
import type { EggProtoImplClass, IAdvice, AdviceInfo } from '@eggjs/tegg-types';

import { CrosscutInfoUtil } from './util/index.ts';

@InnerObjectProto()
export class CrosscutAdviceFactory {
private readonly crosscutAdviceClazzList: Array<EggProtoImplClass<IAdvice>> = [];

Expand Down
4 changes: 1 addition & 3 deletions tegg/core/aop-runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"dependencies": {
"@eggjs/aop-decorator": "workspace:*",
"@eggjs/core-decorator": "workspace:*",
"@eggjs/lifecycle": "workspace:*",
"@eggjs/metadata": "workspace:*",
"@eggjs/tegg-common-util": "workspace:*",
"@eggjs/tegg-runtime": "workspace:*",
Expand All @@ -59,8 +60,5 @@
},
"engines": {
"node": ">=22.18.0"
},
"eggModule": {
"name": "teggAopRuntime"
}
}
27 changes: 27 additions & 0 deletions tegg/core/aop-runtime/src/AopGraphHookRegistrar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { InnerObjectProto } from '@eggjs/core-decorator';
import { LifecyclePostInject } from '@eggjs/lifecycle';
import { GlobalGraph } from '@eggjs/metadata';

import { crossCutGraphHook } from './CrossCutGraphHook.js';
import { pointCutGraphHook } from './PointCutGraphHook.js';
Comment on lines +5 to +6

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

According to the repository's general rules, TypeScript source file imports should use the .ts extension instead of .js to maintain consistency across the codebase.

Suggested change
import { crossCutGraphHook } from './CrossCutGraphHook.js';
import { pointCutGraphHook } from './PointCutGraphHook.js';
import { crossCutGraphHook } from './CrossCutGraphHook.ts';
import { pointCutGraphHook } from './PointCutGraphHook.ts';
References
  1. In this repository, use '.ts' extensions in import/export paths for TypeScript source files to maintain consistency with the existing convention across source and test files.


/**
* Registers the AOP graph build hooks declaratively. Instantiated with the
* InnerObjectLoadUnit, which both hosts run AFTER the business GlobalGraph is
* created and BEFORE build() consumes the hooks — the only valid window.
*/
@InnerObjectProto()
export class AopGraphHookRegistrar {
@LifecyclePostInject()
protected registerGraphHooks(): void {
const globalGraph = GlobalGraph.instance;
if (!globalGraph) {
throw new Error(
'[aop-runtime] GlobalGraph must be created before AopGraphHookRegistrar is instantiated, ' +
'cross-loadUnit crosscut/pointcut weaving would silently never happen',
);
}
globalGraph.registerBuildHook(crossCutGraphHook);
globalGraph.registerBuildHook(pointCutGraphHook);
}
}
3 changes: 2 additions & 1 deletion tegg/core/aop-runtime/src/EggObjectAopHook.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import assert from 'node:assert';

import { Aspect } from '@eggjs/aop-decorator';
import { PrototypeUtil } from '@eggjs/core-decorator';
import { EggObjectLifecycleProto, PrototypeUtil } from '@eggjs/core-decorator';
import { EggContainerFactory } from '@eggjs/tegg-runtime';
import { ASPECT_LIST, InjectType } from '@eggjs/tegg-types';
import type { EggObject, EggObjectLifeCycleContext, LifecycleHook } from '@eggjs/tegg-types';

import { AspectExecutor } from './AspectExecutor.js';

@EggObjectLifecycleProto()
export class EggObjectAopHook implements LifecycleHook<EggObjectLifeCycleContext, EggObject> {
private hijackMethods(obj: any, aspectList: Array<Aspect>) {
for (const aspect of aspectList) {
Expand Down
7 changes: 3 additions & 4 deletions tegg/core/aop-runtime/src/EggPrototypeCrossCutHook.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { CrosscutAdviceFactory, CrosscutInfoUtil } from '@eggjs/aop-decorator';
import { EggPrototypeLifecycleProto, Inject } from '@eggjs/core-decorator';
import type { EggPrototype, EggPrototypeLifecycleContext, LifecycleHook } from '@eggjs/tegg-types';

@EggPrototypeLifecycleProto()
export class EggPrototypeCrossCutHook implements LifecycleHook<EggPrototypeLifecycleContext, EggPrototype> {
@Inject()
private readonly crosscutAdviceFactory: CrosscutAdviceFactory;

constructor(crosscutAdviceFactory: CrosscutAdviceFactory) {
this.crosscutAdviceFactory = crosscutAdviceFactory;
}

async preCreate(ctx: EggPrototypeLifecycleContext): Promise<void> {
if (CrosscutInfoUtil.isCrosscutAdvice(ctx.clazz)) {
this.crosscutAdviceFactory.registerCrossAdviceClazz(ctx.clazz);
Expand Down
7 changes: 3 additions & 4 deletions tegg/core/aop-runtime/src/LoadUnitAopHook.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AspectInfoUtil, AspectMetaBuilder, CrosscutAdviceFactory } from '@eggjs/aop-decorator';
import { Inject, LoadUnitLifecycleProto } from '@eggjs/core-decorator';
import { EggPrototypeFactory, TeggError } from '@eggjs/metadata';
import type {
EggPrototype,
Expand All @@ -8,13 +9,11 @@ import type {
LoadUnitLifecycleContext,
} from '@eggjs/tegg-types';

@LoadUnitLifecycleProto()
export class LoadUnitAopHook implements LifecycleHook<LoadUnitLifecycleContext, LoadUnit> {
@Inject()
private readonly crosscutAdviceFactory: CrosscutAdviceFactory;

constructor(crosscutAdviceFactory: CrosscutAdviceFactory) {
this.crosscutAdviceFactory = crosscutAdviceFactory;
}

async postCreate(_: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise<void> {
for (const proto of loadUnit.iterateEggPrototype()) {
const protoWithClazz = proto as EggPrototypeWithClazz;
Expand Down
1 change: 1 addition & 0 deletions tegg/core/aop-runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './EggObjectAopHook.js';
export * from './EggPrototypeCrossCutHook.js';
export * from './LoadUnitAopHook.js';
export * from './PointCutGraphHook.js';
export * from './AopGraphHookRegistrar.js';
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

exports[`should export stable 1`] = `
{
"AopGraphHookRegistrar": [Function],
"AspectExecutor": [Function],
"EggObjectAopHook": [Function],
"EggPrototypeCrossCutHook": [Function],
Expand Down
21 changes: 12 additions & 9 deletions tegg/core/aop-runtime/test/aop-runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,16 @@ describe('test/aop-runtime.test.ts', () => {
beforeEach(async () => {
crosscutAdviceFactory = new CrosscutAdviceFactory();
eggObjectAopHook = new EggObjectAopHook();
loadUnitAopHook = new LoadUnitAopHook(crosscutAdviceFactory);
eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook(crosscutAdviceFactory);
loadUnitAopHook = new LoadUnitAopHook();
Reflect.set(loadUnitAopHook, 'crosscutAdviceFactory', crosscutAdviceFactory);
eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook();
Reflect.set(eggPrototypeCrossCutHook, 'crosscutAdviceFactory', crosscutAdviceFactory);
EggPrototypeLifecycleUtil.registerLifecycle(eggPrototypeCrossCutHook);
LoadUnitLifecycleUtil.registerLifecycle(loadUnitAopHook);
EggObjectLifecycleUtil.registerLifecycle(eggObjectAopHook);

modules = await CoreTestHelper.prepareModules(
[
path.join(__dirname, '..'),
path.join(__dirname, 'fixtures/modules/hello_succeed'),
path.join(__dirname, 'fixtures/modules/hello_point_cut'),
path.join(__dirname, 'fixtures/modules/state_point_cut'),
Expand Down Expand Up @@ -166,8 +167,10 @@ describe('test/aop-runtime.test.ts', () => {
beforeEach(async () => {
crosscutAdviceFactory = new CrosscutAdviceFactory();
eggObjectAopHook = new EggObjectAopHook();
loadUnitAopHook = new LoadUnitAopHook(crosscutAdviceFactory);
eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook(crosscutAdviceFactory);
loadUnitAopHook = new LoadUnitAopHook();
Reflect.set(loadUnitAopHook, 'crosscutAdviceFactory', crosscutAdviceFactory);
eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook();
Reflect.set(eggPrototypeCrossCutHook, 'crosscutAdviceFactory', crosscutAdviceFactory);
EggPrototypeLifecycleUtil.registerLifecycle(eggPrototypeCrossCutHook);
LoadUnitLifecycleUtil.registerLifecycle(loadUnitAopHook);
EggObjectLifecycleUtil.registerLifecycle(eggObjectAopHook);
Expand All @@ -176,7 +179,6 @@ describe('test/aop-runtime.test.ts', () => {
it('should throw', async () => {
await assert.rejects(async () => {
await CoreTestHelper.prepareModules([
path.join(__dirname, '..'),
path.join(__dirname, 'fixtures/modules/should_throw'),
]);
}, /Aop Advice\(PointcutAdvice\) not found in loadUnits/);
Expand All @@ -193,15 +195,16 @@ describe('test/aop-runtime.test.ts', () => {
beforeEach(async () => {
crosscutAdviceFactory = new CrosscutAdviceFactory();
eggObjectAopHook = new EggObjectAopHook();
loadUnitAopHook = new LoadUnitAopHook(crosscutAdviceFactory);
eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook(crosscutAdviceFactory);
loadUnitAopHook = new LoadUnitAopHook();
Reflect.set(loadUnitAopHook, 'crosscutAdviceFactory', crosscutAdviceFactory);
eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook();
Reflect.set(eggPrototypeCrossCutHook, 'crosscutAdviceFactory', crosscutAdviceFactory);
EggPrototypeLifecycleUtil.registerLifecycle(eggPrototypeCrossCutHook);
LoadUnitLifecycleUtil.registerLifecycle(loadUnitAopHook);
EggObjectLifecycleUtil.registerLifecycle(eggObjectAopHook);

modules = await CoreTestHelper.prepareModules(
[
path.join(__dirname, '..'),
path.join(__dirname, 'fixtures/modules/constructor_inject_aop'),
path.join(__dirname, 'fixtures/modules/hello_point_cut'),
path.join(__dirname, 'fixtures/modules/hello_cross_cut'),
Expand Down
32 changes: 32 additions & 0 deletions tegg/core/core-decorator/src/decorator/EggLifecycleProto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import assert from 'node:assert';

import type { CommonEggLifecycleProtoParams, EggLifecycleProtoParams, EggProtoImplClass } from '@eggjs/tegg-types';

import { PrototypeUtil } from '../util/PrototypeUtil.ts';
import { InnerObjectProto } from './InnerObjectProto.ts';
import type { PrototypeDecorator } from './Prototype.ts';

export function EggLifecycleProto(params: CommonEggLifecycleProtoParams): PrototypeDecorator {
return function (clazz: EggProtoImplClass) {
const { type, ...protoParams } = params || {};
assert(type, 'EggLifecycle decorator should have type property');

InnerObjectProto(protoParams)(clazz);

PrototypeUtil.setIsEggLifecyclePrototype(clazz);
PrototypeUtil.setEggLifecyclePrototypeMetadata(clazz, { type });
};
}

type EggLifecycleProtoDecoratorFactory = (params?: EggLifecycleProtoParams) => PrototypeDecorator;

const createLifecycleProto = (type: CommonEggLifecycleProtoParams['type']): EggLifecycleProtoDecoratorFactory => {
return (params?: EggLifecycleProtoParams) => EggLifecycleProto({ type, ...params });
};

export const LoadUnitLifecycleProto: EggLifecycleProtoDecoratorFactory = createLifecycleProto('LoadUnit');
export const LoadUnitInstanceLifecycleProto: EggLifecycleProtoDecoratorFactory =
createLifecycleProto('LoadUnitInstance');
export const EggObjectLifecycleProto: EggLifecycleProtoDecoratorFactory = createLifecycleProto('EggObject');
export const EggPrototypeLifecycleProto: EggLifecycleProtoDecoratorFactory = createLifecycleProto('EggPrototype');
export const EggContextLifecycleProto: EggLifecycleProtoDecoratorFactory = createLifecycleProto('EggContext');
17 changes: 17 additions & 0 deletions tegg/core/core-decorator/src/decorator/InnerObjectProto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { EGG_INNER_OBJECT_PROTO_IMPL_TYPE } from '@eggjs/tegg-types';
import type { EggProtoImplClass, InnerObjectProtoParams } from '@eggjs/tegg-types';

import { PrototypeUtil } from '../util/PrototypeUtil.ts';
import type { PrototypeDecorator } from './Prototype.ts';
import { SingletonProto } from './SingletonProto.ts';

export function InnerObjectProto(params?: InnerObjectProtoParams): PrototypeDecorator {
return function (clazz: EggProtoImplClass) {
const protoParams = {
protoImplType: EGG_INNER_OBJECT_PROTO_IMPL_TYPE,
...params,
};
SingletonProto(protoParams)(clazz);
PrototypeUtil.setIsEggInnerObject(clazz);
};
}
2 changes: 2 additions & 0 deletions tegg/core/core-decorator/src/decorator/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
export * from './ConfigSource.ts';
export * from './ContextProto.ts';
export * from './EggLifecycleProto.ts';
export * from './EggQualifier.ts';
export * from './InitTypeQualifier.ts';
export * from './Inject.ts';
export * from './InnerObjectProto.ts';
export * from './ModuleQualifier.ts';
export * from './MultiInstanceInfo.ts';
export * from './MultiInstanceProto.ts';
Expand Down
56 changes: 56 additions & 0 deletions tegg/core/core-decorator/src/util/PrototypeUtil.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
type EggLifecycleInfo,
type EggMultiInstanceCallbackPrototypeInfo,
type EggMultiInstancePrototypeInfo,
type EggProtoImplClass,
Expand Down Expand Up @@ -37,6 +38,9 @@ export class PrototypeUtil {
static readonly MULTI_INSTANCE_CONSTRUCTOR_ATTRIBUTES: symbol = Symbol.for(
'EggPrototype#multiInstanceConstructorAttributes',
);
static readonly IS_EGG_INNER_OBJECT: symbol = Symbol.for('EggPrototype#isEggInnerObject');
static readonly IS_EGG_LIFECYCLE_PROTOTYPE: symbol = Symbol.for('EggPrototype#isEggLifecyclePrototype');
static readonly EGG_LIFECYCLE_PROTOTYPE_METADATA: symbol = Symbol.for('EggPrototype#eggLifecyclePrototype#metadata');

/**
* Mark class is egg object prototype
Expand Down Expand Up @@ -70,6 +74,58 @@ export class PrototypeUtil {
return MetadataUtil.getOwnBooleanMetaData(PrototypeUtil.IS_EGG_OBJECT_MULTI_INSTANCE_PROTOTYPE, clazz);
}

/**
* Mark class is egg inner object prototype
* @param {Function} clazz -
*/
static setIsEggInnerObject(clazz: EggProtoImplClass): void {
MetadataUtil.defineMetaData(PrototypeUtil.IS_EGG_INNER_OBJECT, true, clazz);
}

/**
* If class is egg inner object prototype, return true
* @param {Function} clazz -
*/
static isEggInnerObject(clazz: EggProtoImplClass): boolean {
return MetadataUtil.getOwnBooleanMetaData(PrototypeUtil.IS_EGG_INNER_OBJECT, clazz);
}

/**
* Mark class is egg lifecycle prototype
* @param {Function} clazz -
*/
static setIsEggLifecyclePrototype(clazz: EggProtoImplClass): void {
MetadataUtil.defineMetaData(PrototypeUtil.IS_EGG_LIFECYCLE_PROTOTYPE, true, clazz);
}

/**
* If class is egg lifecycle prototype, return true
* @param {Function} clazz -
*/
static isEggLifecyclePrototype(clazz: EggProtoImplClass): boolean {
return MetadataUtil.getOwnBooleanMetaData(PrototypeUtil.IS_EGG_LIFECYCLE_PROTOTYPE, clazz);
}

/**
* Set egg lifecycle prototype metadata, like the lifecycle type
* @param {Function} clazz -
* @param {EggLifecycleInfo} metadata -
*/
static setEggLifecyclePrototypeMetadata(clazz: EggProtoImplClass, metadata: EggLifecycleInfo): void {
MetadataUtil.defineMetaData(PrototypeUtil.EGG_LIFECYCLE_PROTOTYPE_METADATA, metadata, clazz);
}

/**
* Get egg lifecycle prototype metadata
* @param {Function} clazz -
*/
static getEggLifecyclePrototypeMetadata(clazz: EggProtoImplClass): EggLifecycleInfo | undefined {
if (!PrototypeUtil.isEggLifecyclePrototype(clazz)) {
return undefined;
}
return MetadataUtil.getOwnMetaData<EggLifecycleInfo>(PrototypeUtil.EGG_LIFECYCLE_PROTOTYPE_METADATA, clazz);
}

/**
* Get the type of the egg multi-instance prototype.
* @param {Function} clazz -
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ exports[`should export stable 1`] = `
"ConfigSourceQualifierAttribute": Symbol(Qualifier.ConfigSource),
"ContextProto": [Function],
"DEFAULT_PROTO_IMPL_TYPE": "DEFAULT",
"EGG_INNER_OBJECT_PROTO_IMPL_TYPE": "EGG_INNER_OBJECT_PROTOTYPE",
"EggContextLifecycleProto": [Function],
"EggLifecycleProto": [Function],
"EggObjectLifecycleProto": [Function],
"EggPrototypeLifecycleProto": [Function],
"EggQualifier": [Function],
"EggQualifierAttribute": Symbol(Qualifier.Egg),
"EggType": {
Expand All @@ -30,6 +35,9 @@ exports[`should export stable 1`] = `
"CONSTRUCTOR": "CONSTRUCTOR",
"PROPERTY": "PROPERTY",
},
"InnerObjectProto": [Function],
"LoadUnitInstanceLifecycleProto": [Function],
"LoadUnitLifecycleProto": [Function],
"LoadUnitNameQualifierAttribute": Symbol(Qualifier.LoadUnitName),
"MetadataUtil": [Function],
"ModuleQualifier": [Function],
Expand Down
Loading
Loading