@rsdk/grpc.loader 1.0.13
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 +62 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +50 -0
- package/dist/loader.js +64 -0
- package/dist/loader.js.map +1 -0
- package/dist/options.d.ts +14 -0
- package/dist/options.js +28 -0
- package/dist/options.js.map +1 -0
- package/package.json +17 -0
- package/src/index.ts +2 -0
- package/src/loader.ts +65 -0
- package/src/options.ts +52 -0
- package/tsconfig.json +10 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
## 1.0.13 (2023-06-06)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @rsdk/grpc.loader
|
|
9
|
+
|
|
10
|
+
## 1.0.12 (2023-05-23)
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* remove private dependencies from `peerDependencies` section ([0bce6ff](https://github.com/R-Vision/rsdk/commit/0bce6ffb5b699e2ed1dc9aca77cbdbf085267ff7))
|
|
15
|
+
|
|
16
|
+
## 1.0.11 (2023-05-23)
|
|
17
|
+
|
|
18
|
+
**Note:** Version bump only for package @rsdk/grpc.loader
|
|
19
|
+
|
|
20
|
+
## 1.0.10 (2023-05-22)
|
|
21
|
+
|
|
22
|
+
### Bug Fixes
|
|
23
|
+
|
|
24
|
+
* add "access: public" to new packages ([04d5983](https://github.com/R-Vision/rsdk/commit/04d5983cfa3e97c5d8219e07208a0499a8c6bc83))
|
|
25
|
+
|
|
26
|
+
## 1.0.9 (2023-05-21)
|
|
27
|
+
|
|
28
|
+
**Note:** Version bump only for package @rsdk/grpc.loader
|
|
29
|
+
|
|
30
|
+
## 1.0.8 (2023-05-17)
|
|
31
|
+
|
|
32
|
+
**Note:** Version bump only for package @rsdk/grpc.loader
|
|
33
|
+
|
|
34
|
+
## 1.0.7 (2023-05-11)
|
|
35
|
+
|
|
36
|
+
**Note:** Version bump only for package @rsdk/grpc.loader
|
|
37
|
+
|
|
38
|
+
## 1.0.6 (2023-05-11)
|
|
39
|
+
|
|
40
|
+
**Note:** Version bump only for package @rsdk/grpc.loader
|
|
41
|
+
|
|
42
|
+
## 1.0.5 (2023-05-10)
|
|
43
|
+
|
|
44
|
+
**Note:** Version bump only for package @rsdk/grpc.loader
|
|
45
|
+
|
|
46
|
+
## 1.0.4 (2023-05-10)
|
|
47
|
+
|
|
48
|
+
**Note:** Version bump only for package @rsdk/grpc.loader
|
|
49
|
+
|
|
50
|
+
## 1.0.3 (2023-05-10)
|
|
51
|
+
|
|
52
|
+
### Bug Fixes
|
|
53
|
+
|
|
54
|
+
* builtin make not private in npm ([50c83b6](https://github.com/R-Vision/rsdk/commit/50c83b6454f9a89f1e99d3b33a497ce2c029d8c6))
|
|
55
|
+
|
|
56
|
+
## 1.0.2 (2023-05-10)
|
|
57
|
+
|
|
58
|
+
**Note:** Version bump only for package @rsdk/grpc.loader
|
|
59
|
+
|
|
60
|
+
## 1.0.1 (2023-05-07)
|
|
61
|
+
|
|
62
|
+
**Note:** Version bump only for package @rsdk/grpc.loader
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./loader"), exports);
|
|
18
|
+
__exportStar(require("./options"), exports);
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB;AACzB,4CAA0B"}
|
package/dist/loader.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CUSTOM PROTO "LOADER"
|
|
3
|
+
*
|
|
4
|
+
* Стандартный loader, который использует нест (@grpc/proto-loader) требует для работы
|
|
5
|
+
* исходных .proto-файлов. Нам это не подходит, т.к. контракты мы храним в отдельных
|
|
6
|
+
* репозиториях.
|
|
7
|
+
*
|
|
8
|
+
* Данный модуль, подсовывается несту, вместо @grpc/proto-loader.
|
|
9
|
+
*
|
|
10
|
+
* Как это работает:
|
|
11
|
+
*
|
|
12
|
+
* Для работы gRPC-транспорту неста нужны "определения"/"дефинишены" - они же definitions.
|
|
13
|
+
* Определения представляют собой объект такого вида:
|
|
14
|
+
*
|
|
15
|
+
{
|
|
16
|
+
'package1.v1.SomeService': {
|
|
17
|
+
something: {
|
|
18
|
+
path: '/package1.v1.SomeService/Something',
|
|
19
|
+
requestStream: false,
|
|
20
|
+
responseStream: false,
|
|
21
|
+
requestSerialize: ...,
|
|
22
|
+
requestDeserialize: ...,
|
|
23
|
+
responseSerialize: ...,
|
|
24
|
+
responseDeserialize: ...
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
...
|
|
28
|
+
}
|
|
29
|
+
*
|
|
30
|
+
* Первый уровень ключей - <имя grpc-пакета>.<имя grpc-сервиса>.
|
|
31
|
+
* Второй уровень - название gRPC метода (приведённое к camelCase)
|
|
32
|
+
*
|
|
33
|
+
* Подобные объекты генерируется из .proto-файлов при помощи библиотеки
|
|
34
|
+
* @grpc/grpc-js, которую мы используем в нашем генераторе.
|
|
35
|
+
* TODO: ссылку на репозиторий генератора
|
|
36
|
+
*
|
|
37
|
+
* Функция loadSync не предназначена для пробрасывания этих дефинишенов напрямую.
|
|
38
|
+
* Но объект передаваемый в поле loader попадёт в функцию без изменений.
|
|
39
|
+
* Этим фактом мы и пользуемся для проброса массива объектов классы Package,
|
|
40
|
+
* которые содержат всю необходимую информацию.
|
|
41
|
+
*
|
|
42
|
+
* Остётся только смержить данные по всем пакетам в один объект и сделать правильные
|
|
43
|
+
* ключи.
|
|
44
|
+
*
|
|
45
|
+
* NOTE: В первом аргументе, соотвественно, будет undefined, поскольку поле protoPath
|
|
46
|
+
* мы не передаём
|
|
47
|
+
*/
|
|
48
|
+
export declare function loadSync(_file: string | string[], loader: {
|
|
49
|
+
packages: Record<string, any>[];
|
|
50
|
+
}): object;
|
package/dist/loader.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadSync = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* CUSTOM PROTO "LOADER"
|
|
6
|
+
*
|
|
7
|
+
* Стандартный loader, который использует нест (@grpc/proto-loader) требует для работы
|
|
8
|
+
* исходных .proto-файлов. Нам это не подходит, т.к. контракты мы храним в отдельных
|
|
9
|
+
* репозиториях.
|
|
10
|
+
*
|
|
11
|
+
* Данный модуль, подсовывается несту, вместо @grpc/proto-loader.
|
|
12
|
+
*
|
|
13
|
+
* Как это работает:
|
|
14
|
+
*
|
|
15
|
+
* Для работы gRPC-транспорту неста нужны "определения"/"дефинишены" - они же definitions.
|
|
16
|
+
* Определения представляют собой объект такого вида:
|
|
17
|
+
*
|
|
18
|
+
{
|
|
19
|
+
'package1.v1.SomeService': {
|
|
20
|
+
something: {
|
|
21
|
+
path: '/package1.v1.SomeService/Something',
|
|
22
|
+
requestStream: false,
|
|
23
|
+
responseStream: false,
|
|
24
|
+
requestSerialize: ...,
|
|
25
|
+
requestDeserialize: ...,
|
|
26
|
+
responseSerialize: ...,
|
|
27
|
+
responseDeserialize: ...
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
...
|
|
31
|
+
}
|
|
32
|
+
*
|
|
33
|
+
* Первый уровень ключей - <имя grpc-пакета>.<имя grpc-сервиса>.
|
|
34
|
+
* Второй уровень - название gRPC метода (приведённое к camelCase)
|
|
35
|
+
*
|
|
36
|
+
* Подобные объекты генерируется из .proto-файлов при помощи библиотеки
|
|
37
|
+
* @grpc/grpc-js, которую мы используем в нашем генераторе.
|
|
38
|
+
* TODO: ссылку на репозиторий генератора
|
|
39
|
+
*
|
|
40
|
+
* Функция loadSync не предназначена для пробрасывания этих дефинишенов напрямую.
|
|
41
|
+
* Но объект передаваемый в поле loader попадёт в функцию без изменений.
|
|
42
|
+
* Этим фактом мы и пользуемся для проброса массива объектов классы Package,
|
|
43
|
+
* которые содержат всю необходимую информацию.
|
|
44
|
+
*
|
|
45
|
+
* Остётся только смержить данные по всем пакетам в один объект и сделать правильные
|
|
46
|
+
* ключи.
|
|
47
|
+
*
|
|
48
|
+
* NOTE: В первом аргументе, соотвественно, будет undefined, поскольку поле protoPath
|
|
49
|
+
* мы не передаём
|
|
50
|
+
*/
|
|
51
|
+
function loadSync(_file, loader) {
|
|
52
|
+
const result = {};
|
|
53
|
+
for (const pkg of loader.packages) {
|
|
54
|
+
// Берём именно definitions['grpc-js']
|
|
55
|
+
// Есть ещёdefinitions['generic'] - они для другого
|
|
56
|
+
const definitions = pkg.definitions['grpc-js'];
|
|
57
|
+
for (const [service, definition] of Object.entries(definitions)) {
|
|
58
|
+
result[`${pkg.name}.${service}`] = definition;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
exports.loadSync = loadSync;
|
|
64
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,SAAgB,QAAQ,CACtB,KAAwB,EACxB,MAA2C;IAE3C,MAAM,MAAM,GAA4B,EAAE,CAAC;IAE3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,QAAQ,EAAE;QACjC,sCAAsC;QACtC,mDAAmD;QACnD,MAAM,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QAE/C,KAAK,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE;YAC/D,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC,GAAG,UAAU,CAAC;SAC/C;KACF;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAjBD,4BAiBC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { MicroserviceOptions } from '@nestjs/microservices';
|
|
2
|
+
export interface Package {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly definitions: {
|
|
5
|
+
'grpc-js': object;
|
|
6
|
+
generic: object;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export interface GrpcServerOptions {
|
|
10
|
+
address: string;
|
|
11
|
+
maxRecv?: number;
|
|
12
|
+
maxSend?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare const createGrpcOptions: (packages: Package[], options: GrpcServerOptions) => MicroserviceOptions;
|
package/dist/options.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createGrpcOptions = void 0;
|
|
4
|
+
const microservices_1 = require("@nestjs/microservices");
|
|
5
|
+
const createGrpcOptions = (packages, options) => {
|
|
6
|
+
return {
|
|
7
|
+
transport: microservices_1.Transport.GRPC,
|
|
8
|
+
options: {
|
|
9
|
+
loader: {
|
|
10
|
+
packages,
|
|
11
|
+
},
|
|
12
|
+
/**
|
|
13
|
+
* Подменяем дефолтный @grpc/proto-loader реализацией,
|
|
14
|
+
* не требующей исходных .proto-файлов
|
|
15
|
+
*/
|
|
16
|
+
protoLoader: '@rsdk/grpc.loader',
|
|
17
|
+
// Actually package'S'
|
|
18
|
+
package: packages.map((x) => x.name),
|
|
19
|
+
url: options.address,
|
|
20
|
+
channelOptions: {
|
|
21
|
+
'grpc.max_receive_message_length': options.maxRecv,
|
|
22
|
+
'grpc.max_send_message_length': options.maxSend,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
exports.createGrpcOptions = createGrpcOptions;
|
|
28
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":";;;AACA,yDAAkD;AAsB3C,MAAM,iBAAiB,GAAG,CAC/B,QAAmB,EACnB,OAA0B,EACL,EAAE;IACvB,OAAO;QACL,SAAS,EAAE,yBAAS,CAAC,IAAI;QAEzB,OAAO,EAAE;YACP,MAAM,EAAE;gBACN,QAAQ;aACT;YAED;;;eAGG;YACH,WAAW,EAAE,mBAAmB;YAEhC,sBAAsB;YACtB,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YACpC,GAAG,EAAE,OAAO,CAAC,OAAO;YAEpB,cAAc,EAAE;gBACd,iCAAiC,EAAE,OAAO,CAAC,OAAO;gBAClD,8BAA8B,EAAE,OAAO,CAAC,OAAO;aAChD;SACK;KACT,CAAC;AACJ,CAAC,CAAC;AA5BW,QAAA,iBAAiB,qBA4B5B"}
|
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rsdk/grpc.loader",
|
|
3
|
+
"version": "1.0.13",
|
|
4
|
+
"description": "Load generated ts code to nestjs and initialize microservice",
|
|
5
|
+
"license": "Apache License 2.0",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"url": "https://github.com/R-Vision/rsdk"
|
|
11
|
+
},
|
|
12
|
+
"main": "dist/index.js",
|
|
13
|
+
"peerDependencies": {
|
|
14
|
+
"@nestjs/microservices": "^9.0.0"
|
|
15
|
+
},
|
|
16
|
+
"gitHead": "1f40e539993d341b239f78bf2f8b29b44fae9c1e"
|
|
17
|
+
}
|
package/src/index.ts
ADDED
package/src/loader.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CUSTOM PROTO "LOADER"
|
|
3
|
+
*
|
|
4
|
+
* Стандартный loader, который использует нест (@grpc/proto-loader) требует для работы
|
|
5
|
+
* исходных .proto-файлов. Нам это не подходит, т.к. контракты мы храним в отдельных
|
|
6
|
+
* репозиториях.
|
|
7
|
+
*
|
|
8
|
+
* Данный модуль, подсовывается несту, вместо @grpc/proto-loader.
|
|
9
|
+
*
|
|
10
|
+
* Как это работает:
|
|
11
|
+
*
|
|
12
|
+
* Для работы gRPC-транспорту неста нужны "определения"/"дефинишены" - они же definitions.
|
|
13
|
+
* Определения представляют собой объект такого вида:
|
|
14
|
+
*
|
|
15
|
+
{
|
|
16
|
+
'package1.v1.SomeService': {
|
|
17
|
+
something: {
|
|
18
|
+
path: '/package1.v1.SomeService/Something',
|
|
19
|
+
requestStream: false,
|
|
20
|
+
responseStream: false,
|
|
21
|
+
requestSerialize: ...,
|
|
22
|
+
requestDeserialize: ...,
|
|
23
|
+
responseSerialize: ...,
|
|
24
|
+
responseDeserialize: ...
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
...
|
|
28
|
+
}
|
|
29
|
+
*
|
|
30
|
+
* Первый уровень ключей - <имя grpc-пакета>.<имя grpc-сервиса>.
|
|
31
|
+
* Второй уровень - название gRPC метода (приведённое к camelCase)
|
|
32
|
+
*
|
|
33
|
+
* Подобные объекты генерируется из .proto-файлов при помощи библиотеки
|
|
34
|
+
* @grpc/grpc-js, которую мы используем в нашем генераторе.
|
|
35
|
+
* TODO: ссылку на репозиторий генератора
|
|
36
|
+
*
|
|
37
|
+
* Функция loadSync не предназначена для пробрасывания этих дефинишенов напрямую.
|
|
38
|
+
* Но объект передаваемый в поле loader попадёт в функцию без изменений.
|
|
39
|
+
* Этим фактом мы и пользуемся для проброса массива объектов классы Package,
|
|
40
|
+
* которые содержат всю необходимую информацию.
|
|
41
|
+
*
|
|
42
|
+
* Остётся только смержить данные по всем пакетам в один объект и сделать правильные
|
|
43
|
+
* ключи.
|
|
44
|
+
*
|
|
45
|
+
* NOTE: В первом аргументе, соотвественно, будет undefined, поскольку поле protoPath
|
|
46
|
+
* мы не передаём
|
|
47
|
+
*/
|
|
48
|
+
export function loadSync(
|
|
49
|
+
_file: string | string[],
|
|
50
|
+
loader: { packages: Record<string, any>[] },
|
|
51
|
+
): object {
|
|
52
|
+
const result: Record<string, unknown> = {};
|
|
53
|
+
|
|
54
|
+
for (const pkg of loader.packages) {
|
|
55
|
+
// Берём именно definitions['grpc-js']
|
|
56
|
+
// Есть ещёdefinitions['generic'] - они для другого
|
|
57
|
+
const definitions = pkg.definitions['grpc-js'];
|
|
58
|
+
|
|
59
|
+
for (const [service, definition] of Object.entries(definitions)) {
|
|
60
|
+
result[`${pkg.name}.${service}`] = definition;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return result;
|
|
65
|
+
}
|
package/src/options.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { MicroserviceOptions } from '@nestjs/microservices';
|
|
2
|
+
import { Transport } from '@nestjs/microservices';
|
|
3
|
+
|
|
4
|
+
// Represents GRPC package
|
|
5
|
+
export interface Package {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly definitions: {
|
|
8
|
+
'grpc-js': object;
|
|
9
|
+
generic: object;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface GrpcServerOptions {
|
|
14
|
+
// Grpc server address (host:port)
|
|
15
|
+
address: string;
|
|
16
|
+
|
|
17
|
+
// Max gRPC message size (receive)
|
|
18
|
+
maxRecv?: number;
|
|
19
|
+
|
|
20
|
+
// Max gRPC message size (send)
|
|
21
|
+
maxSend?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const createGrpcOptions = (
|
|
25
|
+
packages: Package[],
|
|
26
|
+
options: GrpcServerOptions,
|
|
27
|
+
): MicroserviceOptions => {
|
|
28
|
+
return {
|
|
29
|
+
transport: Transport.GRPC,
|
|
30
|
+
|
|
31
|
+
options: {
|
|
32
|
+
loader: {
|
|
33
|
+
packages,
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Подменяем дефолтный @grpc/proto-loader реализацией,
|
|
38
|
+
* не требующей исходных .proto-файлов
|
|
39
|
+
*/
|
|
40
|
+
protoLoader: '@rsdk/grpc.loader',
|
|
41
|
+
|
|
42
|
+
// Actually package'S'
|
|
43
|
+
package: packages.map((x) => x.name),
|
|
44
|
+
url: options.address,
|
|
45
|
+
|
|
46
|
+
channelOptions: {
|
|
47
|
+
'grpc.max_receive_message_length': options.maxRecv,
|
|
48
|
+
'grpc.max_send_message_length': options.maxSend,
|
|
49
|
+
},
|
|
50
|
+
} as any,
|
|
51
|
+
};
|
|
52
|
+
};
|