@rsdk/nest-tools 3.1.0 → 3.4.1-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/exception.d.ts +11 -0
- package/dist/exception.js +26 -0
- package/dist/nest-definition.d.ts +2 -2
- package/dist/nest-definition.iterator.d.ts +7 -3
- package/dist/nest-definition.iterator.js +20 -4
- package/dist/nest-definition.js +0 -8
- package/package.json +2 -2
- package/src/exception.ts +20 -0
- package/src/nest-definition.iterator.ts +59 -6
- package/src/nest-definition.ts +7 -10
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [3.4.1-next.0](https://github.com/R-Vision/rsdk/compare/v3.4.0...v3.4.1-next.0) (2023-09-01)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* added correctly exception and warn on incorrectly value in module tree ([2349782](https://github.com/R-Vision/rsdk/commit/234978226ff9a41a9781fc773d35f865e09ce371))
|
|
11
|
+
|
|
6
12
|
## [3.1.0](https://github.com/R-Vision/rsdk/compare/v3.1.0-next.3...v3.1.0) (2023-08-09)
|
|
7
13
|
|
|
8
14
|
**Note:** Version bump only for package @rsdk/nest-tools
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare abstract class Exception extends Error {
|
|
2
|
+
name: string;
|
|
3
|
+
}
|
|
4
|
+
export declare class FalsyValueInNestModuleTree extends Exception {
|
|
5
|
+
readonly moduleValue: unknown;
|
|
6
|
+
constructor(moduleValue: unknown);
|
|
7
|
+
}
|
|
8
|
+
export declare class UnexpectedNestModuleDefinitionValue extends Exception {
|
|
9
|
+
readonly moduleValue: unknown;
|
|
10
|
+
constructor(moduleValue: unknown);
|
|
11
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UnexpectedNestModuleDefinitionValue = exports.FalsyValueInNestModuleTree = exports.Exception = void 0;
|
|
4
|
+
class Exception extends Error {
|
|
5
|
+
name = this.constructor.name;
|
|
6
|
+
}
|
|
7
|
+
exports.Exception = Exception;
|
|
8
|
+
class FalsyValueInNestModuleTree extends Exception {
|
|
9
|
+
moduleValue;
|
|
10
|
+
constructor(moduleValue) {
|
|
11
|
+
const message = moduleValue === undefined
|
|
12
|
+
? `Undefined was found in the tree of the test module, a cyclic dependence is possible`
|
|
13
|
+
: `Falsy value was found in nest module tree: ${moduleValue}`;
|
|
14
|
+
super(message);
|
|
15
|
+
this.moduleValue = moduleValue;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.FalsyValueInNestModuleTree = FalsyValueInNestModuleTree;
|
|
19
|
+
class UnexpectedNestModuleDefinitionValue extends Exception {
|
|
20
|
+
moduleValue;
|
|
21
|
+
constructor(moduleValue) {
|
|
22
|
+
super();
|
|
23
|
+
this.moduleValue = moduleValue;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.UnexpectedNestModuleDefinitionValue = UnexpectedNestModuleDefinitionValue;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DynamicModule, Provider, Type } from '@nestjs/common';
|
|
1
|
+
import type { DynamicModule, InjectionToken, Provider, Type } from '@nestjs/common';
|
|
2
2
|
export type NestModuleDefinition = DynamicModule | Type;
|
|
3
3
|
/**
|
|
4
4
|
* Хелпер для работы с объявлением модуля nest.
|
|
@@ -10,6 +10,6 @@ export declare class NestDefinition {
|
|
|
10
10
|
getProviders(): Provider[] | undefined;
|
|
11
11
|
getImports(): NestModuleDefinition[] | undefined;
|
|
12
12
|
getControllers(): Type[] | undefined;
|
|
13
|
-
getExports(): Type[] | undefined;
|
|
13
|
+
getExports(): (Type | InjectionToken)[] | undefined;
|
|
14
14
|
private reflectMetadata;
|
|
15
15
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import type { Provider } from '@nestjs/common';
|
|
1
|
+
import type { Abstract, ClassProvider, DynamicModule, ExistingProvider, FactoryProvider, InjectionToken, Provider, Type, ValueProvider } from '@nestjs/common';
|
|
2
2
|
import type { Promisable } from 'type-fest';
|
|
3
3
|
import { NestDefinitionType } from './constants';
|
|
4
4
|
import type { NestModuleDefinition } from './nest-definition';
|
|
5
|
+
export declare class Assert {
|
|
6
|
+
static isObjectOrCtor(nestDefinition: DynamicModule | Type<any> | ClassProvider<any> | ValueProvider<any> | FactoryProvider<any> | ExistingProvider<any> | string | symbol | Abstract<any>): nestDefinition is DynamicModule | Type<any> | ClassProvider<any> | ValueProvider<any> | FactoryProvider<any> | ExistingProvider<any> | Abstract<any> | Function;
|
|
7
|
+
static isObject(v: unknown): v is object;
|
|
8
|
+
}
|
|
5
9
|
export declare class NestDefinitionIterator {
|
|
6
10
|
private nestModuleDefinition;
|
|
7
11
|
constructor(nestModuleDefinition: Promisable<NestModuleDefinition>);
|
|
@@ -9,6 +13,6 @@ export declare class NestDefinitionIterator {
|
|
|
9
13
|
* By default dont iterate by exports
|
|
10
14
|
* @param type
|
|
11
15
|
*/
|
|
12
|
-
iterate(type?: NestDefinitionType): AsyncIterableIterator<NestModuleDefinition | Provider>;
|
|
13
|
-
find<R extends NestModuleDefinition | Provider>(predicate: (definition: NestModuleDefinition | Provider) => boolean, type?: NestDefinitionType): Promise<R | null>;
|
|
16
|
+
iterate(type?: NestDefinitionType): AsyncIterableIterator<NestModuleDefinition | Provider | InjectionToken>;
|
|
17
|
+
find<R extends NestModuleDefinition | Provider>(predicate: (definition: NestModuleDefinition | Provider | Function) => boolean, type?: NestDefinitionType): Promise<R | null>;
|
|
14
18
|
}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.NestDefinitionIterator = void 0;
|
|
3
|
+
exports.NestDefinitionIterator = exports.Assert = void 0;
|
|
4
4
|
const constants_1 = require("./constants");
|
|
5
|
+
const exception_1 = require("./exception");
|
|
5
6
|
const nest_definition_1 = require("./nest-definition");
|
|
7
|
+
class Assert {
|
|
8
|
+
static isObjectOrCtor(nestDefinition) {
|
|
9
|
+
return ['object', 'function'].includes(typeof nestDefinition);
|
|
10
|
+
}
|
|
11
|
+
static isObject(v) {
|
|
12
|
+
return typeof v === 'object';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.Assert = Assert;
|
|
6
16
|
class NestDefinitionIterator {
|
|
7
17
|
nestModuleDefinition;
|
|
8
18
|
constructor(nestModuleDefinition) {
|
|
@@ -14,16 +24,19 @@ class NestDefinitionIterator {
|
|
|
14
24
|
*/
|
|
15
25
|
async *iterate(type) {
|
|
16
26
|
const iterateAll = type === undefined;
|
|
17
|
-
const
|
|
27
|
+
const iterateByModule = iterateAll || type & constants_1.NestDefinitionType.Module;
|
|
18
28
|
const awaitedNestModuleDefinition = await this.nestModuleDefinition;
|
|
19
|
-
if (
|
|
29
|
+
if (!awaitedNestModuleDefinition) {
|
|
30
|
+
throw new exception_1.FalsyValueInNestModuleTree(awaitedNestModuleDefinition);
|
|
31
|
+
}
|
|
32
|
+
if (iterateByModule) {
|
|
20
33
|
yield awaitedNestModuleDefinition;
|
|
21
34
|
if ('module' in awaitedNestModuleDefinition) {
|
|
22
35
|
yield awaitedNestModuleDefinition.module;
|
|
23
36
|
}
|
|
24
37
|
}
|
|
25
38
|
const nestDefinition = new nest_definition_1.NestDefinition(awaitedNestModuleDefinition);
|
|
26
|
-
const nestImports =
|
|
39
|
+
const nestImports = nestDefinition.getImports();
|
|
27
40
|
const iterateProviders = iterateAll || type & constants_1.NestDefinitionType.Providers;
|
|
28
41
|
if (iterateProviders) {
|
|
29
42
|
const providers = nestDefinition.getProviders();
|
|
@@ -51,6 +64,9 @@ class NestDefinitionIterator {
|
|
|
51
64
|
}
|
|
52
65
|
async find(predicate, type) {
|
|
53
66
|
for await (const definition of this.iterate(type)) {
|
|
67
|
+
if (!Assert.isObjectOrCtor(definition)) {
|
|
68
|
+
throw new exception_1.UnexpectedNestModuleDefinitionValue(definition);
|
|
69
|
+
}
|
|
54
70
|
const isNeeded = predicate(definition);
|
|
55
71
|
if (isNeeded) {
|
|
56
72
|
return definition;
|
package/dist/nest-definition.js
CHANGED
|
@@ -13,37 +13,30 @@ class NestDefinition {
|
|
|
13
13
|
this.definition = definition;
|
|
14
14
|
}
|
|
15
15
|
getProviders() {
|
|
16
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
17
16
|
return scanner_1.DependenciesScanner.prototype.isDynamicModule(this.definition)
|
|
18
17
|
? [
|
|
19
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
20
18
|
...this.reflectMetadata(constants_1.MODULE_METADATA.PROVIDERS, this.definition.module),
|
|
21
19
|
...(this.definition.providers || []),
|
|
22
20
|
]
|
|
23
21
|
: this.reflectMetadata(constants_1.MODULE_METADATA.PROVIDERS, this.definition);
|
|
24
22
|
}
|
|
25
23
|
getImports() {
|
|
26
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
27
24
|
return scanner_1.DependenciesScanner.prototype.isDynamicModule(this.definition)
|
|
28
25
|
? [
|
|
29
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
30
26
|
...this.reflectMetadata(constants_1.MODULE_METADATA.IMPORTS, this.definition.module),
|
|
31
27
|
...(this.definition.imports || []),
|
|
32
28
|
]
|
|
33
29
|
: this.reflectMetadata(constants_1.MODULE_METADATA.IMPORTS, this.definition);
|
|
34
30
|
}
|
|
35
31
|
getControllers() {
|
|
36
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
37
32
|
return scanner_1.DependenciesScanner.prototype.isDynamicModule(this.definition)
|
|
38
33
|
? [
|
|
39
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
40
34
|
...this.reflectMetadata(constants_1.MODULE_METADATA.CONTROLLERS, this.definition.module),
|
|
41
35
|
...(this.definition.controllers || []),
|
|
42
36
|
]
|
|
43
37
|
: this.reflectMetadata(constants_1.MODULE_METADATA.CONTROLLERS, this.definition);
|
|
44
38
|
}
|
|
45
39
|
getExports() {
|
|
46
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
47
40
|
return scanner_1.DependenciesScanner.prototype.isDynamicModule(this.definition)
|
|
48
41
|
? [
|
|
49
42
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
@@ -53,7 +46,6 @@ class NestDefinition {
|
|
|
53
46
|
: this.reflectMetadata(constants_1.MODULE_METADATA.EXPORTS, this.definition);
|
|
54
47
|
}
|
|
55
48
|
reflectMetadata(metadataKey, metatype) {
|
|
56
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
57
49
|
return Reflect.getMetadata(metadataKey, metatype) || [];
|
|
58
50
|
}
|
|
59
51
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rsdk/nest-tools",
|
|
3
|
-
"version": "3.1.0",
|
|
3
|
+
"version": "3.4.1-next.0",
|
|
4
4
|
"license": "Apache License 2.0",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -10,5 +10,5 @@
|
|
|
10
10
|
"@nestjs/common": "^10.1.3",
|
|
11
11
|
"@nestjs/core": "^10.1.3"
|
|
12
12
|
},
|
|
13
|
-
"gitHead": "
|
|
13
|
+
"gitHead": "ea16db30e841625857ace62dff377e2c5a952980"
|
|
14
14
|
}
|
package/src/exception.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export abstract class Exception extends Error {
|
|
2
|
+
override name = this.constructor.name;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export class FalsyValueInNestModuleTree extends Exception {
|
|
6
|
+
constructor(readonly moduleValue: unknown) {
|
|
7
|
+
const message =
|
|
8
|
+
moduleValue === undefined
|
|
9
|
+
? `Undefined was found in the tree of the test module, a cyclic dependence is possible`
|
|
10
|
+
: `Falsy value was found in nest module tree: ${moduleValue}`;
|
|
11
|
+
|
|
12
|
+
super(message);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class UnexpectedNestModuleDefinitionValue extends Exception {
|
|
17
|
+
constructor(readonly moduleValue: unknown) {
|
|
18
|
+
super();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,10 +1,54 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
Abstract,
|
|
3
|
+
ClassProvider,
|
|
4
|
+
DynamicModule,
|
|
5
|
+
ExistingProvider,
|
|
6
|
+
FactoryProvider,
|
|
7
|
+
InjectionToken,
|
|
8
|
+
Provider,
|
|
9
|
+
Type,
|
|
10
|
+
ValueProvider,
|
|
11
|
+
} from '@nestjs/common';
|
|
2
12
|
import type { Promisable } from 'type-fest';
|
|
3
13
|
|
|
4
14
|
import { NestDefinitionType } from './constants';
|
|
15
|
+
import {
|
|
16
|
+
FalsyValueInNestModuleTree,
|
|
17
|
+
UnexpectedNestModuleDefinitionValue,
|
|
18
|
+
} from './exception';
|
|
5
19
|
import type { NestModuleDefinition } from './nest-definition';
|
|
6
20
|
import { NestDefinition } from './nest-definition';
|
|
7
21
|
|
|
22
|
+
export class Assert {
|
|
23
|
+
static isObjectOrCtor(
|
|
24
|
+
nestDefinition:
|
|
25
|
+
| DynamicModule
|
|
26
|
+
| Type<any>
|
|
27
|
+
| ClassProvider<any>
|
|
28
|
+
| ValueProvider<any>
|
|
29
|
+
| FactoryProvider<any>
|
|
30
|
+
| ExistingProvider<any>
|
|
31
|
+
| string
|
|
32
|
+
| symbol
|
|
33
|
+
| Abstract<any>,
|
|
34
|
+
): nestDefinition is
|
|
35
|
+
| DynamicModule
|
|
36
|
+
| Type<any>
|
|
37
|
+
| ClassProvider<any>
|
|
38
|
+
| ValueProvider<any>
|
|
39
|
+
| FactoryProvider<any>
|
|
40
|
+
| ExistingProvider<any>
|
|
41
|
+
| Abstract<any>
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
43
|
+
| Function {
|
|
44
|
+
return ['object', 'function'].includes(typeof nestDefinition);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static isObject(v: unknown): v is object {
|
|
48
|
+
return typeof v === 'object';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
8
52
|
export class NestDefinitionIterator {
|
|
9
53
|
constructor(private nestModuleDefinition: Promisable<NestModuleDefinition>) {}
|
|
10
54
|
|
|
@@ -14,11 +58,14 @@ export class NestDefinitionIterator {
|
|
|
14
58
|
*/
|
|
15
59
|
async *iterate(
|
|
16
60
|
type?: NestDefinitionType,
|
|
17
|
-
): AsyncIterableIterator<NestModuleDefinition | Provider> {
|
|
61
|
+
): AsyncIterableIterator<NestModuleDefinition | Provider | InjectionToken> {
|
|
18
62
|
const iterateAll = type === undefined;
|
|
19
|
-
const
|
|
63
|
+
const iterateByModule = iterateAll || type & NestDefinitionType.Module;
|
|
20
64
|
const awaitedNestModuleDefinition = await this.nestModuleDefinition;
|
|
21
|
-
if (
|
|
65
|
+
if (!awaitedNestModuleDefinition) {
|
|
66
|
+
throw new FalsyValueInNestModuleTree(awaitedNestModuleDefinition);
|
|
67
|
+
}
|
|
68
|
+
if (iterateByModule) {
|
|
22
69
|
yield awaitedNestModuleDefinition;
|
|
23
70
|
if ('module' in awaitedNestModuleDefinition) {
|
|
24
71
|
yield awaitedNestModuleDefinition.module;
|
|
@@ -26,7 +73,7 @@ export class NestDefinitionIterator {
|
|
|
26
73
|
}
|
|
27
74
|
const nestDefinition = new NestDefinition(awaitedNestModuleDefinition);
|
|
28
75
|
|
|
29
|
-
const nestImports =
|
|
76
|
+
const nestImports = nestDefinition.getImports();
|
|
30
77
|
|
|
31
78
|
const iterateProviders = iterateAll || type & NestDefinitionType.Providers;
|
|
32
79
|
if (iterateProviders) {
|
|
@@ -65,10 +112,16 @@ export class NestDefinitionIterator {
|
|
|
65
112
|
}
|
|
66
113
|
|
|
67
114
|
async find<R extends NestModuleDefinition | Provider>(
|
|
68
|
-
predicate: (
|
|
115
|
+
predicate: (
|
|
116
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
117
|
+
definition: NestModuleDefinition | Provider | Function,
|
|
118
|
+
) => boolean,
|
|
69
119
|
type?: NestDefinitionType,
|
|
70
120
|
): Promise<R | null> {
|
|
71
121
|
for await (const definition of this.iterate(type)) {
|
|
122
|
+
if (!Assert.isObjectOrCtor(definition)) {
|
|
123
|
+
throw new UnexpectedNestModuleDefinitionValue(definition);
|
|
124
|
+
}
|
|
72
125
|
const isNeeded = predicate(definition);
|
|
73
126
|
if (isNeeded) {
|
|
74
127
|
return definition as R;
|
package/src/nest-definition.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
DynamicModule,
|
|
3
|
+
InjectionToken,
|
|
4
|
+
Provider,
|
|
5
|
+
Type,
|
|
6
|
+
} from '@nestjs/common';
|
|
2
7
|
import { MODULE_METADATA } from '@nestjs/common/constants';
|
|
3
8
|
import { DependenciesScanner } from '@nestjs/core/scanner';
|
|
4
9
|
|
|
@@ -12,10 +17,8 @@ export class NestDefinition {
|
|
|
12
17
|
constructor(private definition: NestModuleDefinition) {}
|
|
13
18
|
|
|
14
19
|
getProviders(): Provider[] | undefined {
|
|
15
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
16
20
|
return DependenciesScanner.prototype.isDynamicModule(this.definition)
|
|
17
21
|
? [
|
|
18
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
19
22
|
...this.reflectMetadata(
|
|
20
23
|
MODULE_METADATA.PROVIDERS,
|
|
21
24
|
this.definition.module,
|
|
@@ -26,10 +29,8 @@ export class NestDefinition {
|
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
getImports(): NestModuleDefinition[] | undefined {
|
|
29
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
30
32
|
return DependenciesScanner.prototype.isDynamicModule(this.definition)
|
|
31
33
|
? [
|
|
32
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
33
34
|
...this.reflectMetadata(
|
|
34
35
|
MODULE_METADATA.IMPORTS,
|
|
35
36
|
this.definition.module,
|
|
@@ -40,10 +41,8 @@ export class NestDefinition {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
getControllers(): Type[] | undefined {
|
|
43
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
44
44
|
return DependenciesScanner.prototype.isDynamicModule(this.definition)
|
|
45
45
|
? [
|
|
46
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
47
46
|
...this.reflectMetadata(
|
|
48
47
|
MODULE_METADATA.CONTROLLERS,
|
|
49
48
|
this.definition.module,
|
|
@@ -53,8 +52,7 @@ export class NestDefinition {
|
|
|
53
52
|
: this.reflectMetadata(MODULE_METADATA.CONTROLLERS, this.definition);
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
getExports(): Type[] | undefined {
|
|
57
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
55
|
+
getExports(): (Type | InjectionToken)[] | undefined {
|
|
58
56
|
return DependenciesScanner.prototype.isDynamicModule(this.definition)
|
|
59
57
|
? [
|
|
60
58
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
@@ -68,7 +66,6 @@ export class NestDefinition {
|
|
|
68
66
|
}
|
|
69
67
|
|
|
70
68
|
private reflectMetadata<T = any>(metadataKey: string, metatype: Type): T[] {
|
|
71
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
72
69
|
return Reflect.getMetadata(metadataKey, metatype) || [];
|
|
73
70
|
}
|
|
74
71
|
}
|