@hastehaul/common 1.0.11 → 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/build/connections-wrappers/nats-wrapper.d.ts +8 -0
- package/build/connections-wrappers/nats-wrapper.js +31 -0
- package/build/connections-wrappers/redis-connection-wrapper.d.ts +17 -0
- package/build/connections-wrappers/redis-connection-wrapper.js +17 -0
- package/build/connections-wrappers/socket-connection-wrapper.d.ts +20 -1
- package/build/connections-wrappers/socket-connection-wrapper.js +19 -0
- package/build/events/base/base-consumer.d.ts +26 -0
- package/build/events/base/base-consumer.js +96 -0
- package/build/events/base/base-publisher.d.ts +14 -0
- package/build/events/base/base-publisher.js +32 -0
- package/build/events/base/base-stream.d.ts +13 -0
- package/build/events/base/base-stream.js +28 -0
- package/build/events/common/enums/durable-names/index.d.ts +3 -0
- package/build/events/common/enums/durable-names/index.js +2 -0
- package/build/events/common/enums/durable-names/order-dname.d.ts +5 -0
- package/build/events/common/enums/durable-names/order-dname.js +9 -0
- package/build/events/common/enums/durable-names/payment-dname.d.ts +4 -0
- package/build/events/common/enums/durable-names/payment-dname.js +8 -0
- package/build/events/common/enums/index.d.ts +3 -0
- package/build/events/common/enums/index.js +19 -0
- package/build/events/common/enums/streams/index.d.ts +3 -0
- package/build/events/common/enums/streams/index.js +2 -0
- package/build/events/common/enums/streams/order-stream.d.ts +3 -0
- package/build/events/common/enums/streams/order-stream.js +7 -0
- package/build/events/common/enums/streams/payment-stream.d.ts +3 -0
- package/build/events/common/enums/streams/payment-stream.js +7 -0
- package/build/events/common/enums/subjects/index.d.ts +3 -0
- package/build/events/common/enums/subjects/index.js +2 -0
- package/build/events/common/enums/subjects/main-subjects.d.ts +10 -0
- package/build/events/common/enums/subjects/main-subjects.js +11 -0
- package/build/events/common/enums/subjects/order-subjects.d.ts +5 -0
- package/build/events/common/enums/subjects/order-subjects.js +9 -0
- package/build/events/common/enums/subjects/payment-subjects.d.ts +4 -0
- package/build/events/common/enums/subjects/payment-subjects.js +8 -0
- package/build/events/common/interfaces/order-events-interfaces/order-cancelled-event.d.ts +15 -0
- package/build/events/common/interfaces/order-events-interfaces/order-cancelled-event.js +2 -0
- package/build/events/common/interfaces/order-events-interfaces/order-completed-event.d.ts +15 -0
- package/build/events/common/interfaces/order-events-interfaces/order-completed-event.js +2 -0
- package/build/events/common/interfaces/order-events-interfaces/order-requested-event.d.ts +15 -0
- package/build/events/common/interfaces/order-events-interfaces/order-requested-event.js +2 -0
- package/build/events/common/interfaces/payment-event-interfaces/payment-created-event.d.ts +0 -0
- package/build/events/common/interfaces/payment-event-interfaces/payment-created-event.js +1 -0
- package/build/events/common/interfaces/payment-event-interfaces/payment-failed-event.d.ts +0 -0
- package/build/events/common/interfaces/payment-event-interfaces/payment-failed-event.js +1 -0
- package/build/events/common/interfaces/stream-interfaces/order-stream-interface.d.ts +6 -0
- package/build/events/common/interfaces/stream-interfaces/order-stream-interface.js +2 -0
- package/build/middlewares/current-user.d.ts +9 -0
- package/build/middlewares/current-user.js +18 -0
- package/build/middlewares/error-handler.d.ts +8 -0
- package/build/middlewares/error-handler.js +8 -0
- package/build/utils/base_classes/jobs/base-producers.d.ts +16 -0
- package/build/utils/base_classes/jobs/base-producers.js +9 -1
- package/build/utils/base_classes/jobs/base-worker.d.ts +16 -0
- package/build/utils/base_classes/jobs/base-worker.js +11 -0
- package/build/utils/random-codes.d.ts +12 -0
- package/build/utils/random-codes.js +52 -0
- package/build/utils/read-dir.js +0 -10
- package/package.json +2 -1
@@ -0,0 +1,31 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9
|
+
});
|
10
|
+
};
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
12
|
+
exports.natsWrapper = void 0;
|
13
|
+
const nats_1 = require("nats");
|
14
|
+
class NatsWrapper {
|
15
|
+
get client() {
|
16
|
+
if (!this._client)
|
17
|
+
throw new Error("Cannot access NATS CLIENT before connecting");
|
18
|
+
return this._client;
|
19
|
+
}
|
20
|
+
connect(opts) {
|
21
|
+
return __awaiter(this, void 0, void 0, function* () {
|
22
|
+
try {
|
23
|
+
this._client = yield (0, nats_1.connect)(opts);
|
24
|
+
}
|
25
|
+
catch (error) {
|
26
|
+
console.log(`error connecting to ${JSON.stringify(opts === null || opts === void 0 ? void 0 : opts.servers)}`);
|
27
|
+
}
|
28
|
+
});
|
29
|
+
}
|
30
|
+
}
|
31
|
+
exports.natsWrapper = new NatsWrapper();
|
@@ -1,9 +1,26 @@
|
|
1
1
|
import IORedis from "ioredis";
|
2
|
+
/**
|
3
|
+
* A wrapper class for the Redis client to manage connections and provide a centralized client instance.
|
4
|
+
*/
|
2
5
|
export declare class RedisWrapper {
|
3
6
|
private _redisClient?;
|
4
7
|
private host;
|
5
8
|
private port;
|
9
|
+
/**
|
10
|
+
* Get the Redis client instance.
|
11
|
+
*
|
12
|
+
* @throws {Error} - Throws an error if the Redis client is accessed before connecting.
|
13
|
+
* @returns {IORedis} - The Redis client instance.
|
14
|
+
*/
|
6
15
|
get client(): IORedis;
|
16
|
+
/**
|
17
|
+
* Connect to the Redis server using the specified host and port.
|
18
|
+
*
|
19
|
+
* The client instance will be stored in the `_redisClient` property for later access.
|
20
|
+
*/
|
7
21
|
connect(): void;
|
8
22
|
}
|
23
|
+
/**
|
24
|
+
* Singleton instance of the RedisWrapper class to provide a centralized Redis client connection.
|
25
|
+
*/
|
9
26
|
export declare const redisWrapper: RedisWrapper;
|
@@ -5,16 +5,30 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
6
|
exports.redisWrapper = exports.RedisWrapper = void 0;
|
7
7
|
const ioredis_1 = __importDefault(require("ioredis"));
|
8
|
+
/**
|
9
|
+
* A wrapper class for the Redis client to manage connections and provide a centralized client instance.
|
10
|
+
*/
|
8
11
|
class RedisWrapper {
|
9
12
|
constructor() {
|
10
13
|
this.host = "customer-redis-srv";
|
11
14
|
this.port = 6379;
|
12
15
|
}
|
16
|
+
/**
|
17
|
+
* Get the Redis client instance.
|
18
|
+
*
|
19
|
+
* @throws {Error} - Throws an error if the Redis client is accessed before connecting.
|
20
|
+
* @returns {IORedis} - The Redis client instance.
|
21
|
+
*/
|
13
22
|
get client() {
|
14
23
|
if (!this._redisClient)
|
15
24
|
throw new Error("Cannot access Redis Client before connecting");
|
16
25
|
return this._redisClient;
|
17
26
|
}
|
27
|
+
/**
|
28
|
+
* Connect to the Redis server using the specified host and port.
|
29
|
+
*
|
30
|
+
* The client instance will be stored in the `_redisClient` property for later access.
|
31
|
+
*/
|
18
32
|
connect() {
|
19
33
|
this._redisClient = new ioredis_1.default({ host: this.host, port: this.port, maxRetriesPerRequest: null });
|
20
34
|
this._redisClient.on("connect", function () {
|
@@ -26,4 +40,7 @@ class RedisWrapper {
|
|
26
40
|
}
|
27
41
|
}
|
28
42
|
exports.RedisWrapper = RedisWrapper;
|
43
|
+
/**
|
44
|
+
* Singleton instance of the RedisWrapper class to provide a centralized Redis client connection.
|
45
|
+
*/
|
29
46
|
exports.redisWrapper = new RedisWrapper();
|
@@ -1,9 +1,28 @@
|
|
1
1
|
/// <reference types="node" />
|
2
2
|
import http from "http";
|
3
3
|
import { Server } from 'socket.io';
|
4
|
+
/**
|
5
|
+
* A wrapper class for the Socket.IO server to manage connections and provide a centralized server instance.
|
6
|
+
*/
|
4
7
|
export declare class SocketWrapper {
|
5
8
|
protected _io?: Server;
|
6
|
-
|
9
|
+
/**
|
10
|
+
* Get the Socket.IO server instance.
|
11
|
+
*
|
12
|
+
* @throws {Error} - Throws an error if the Socket.IO server is accessed before connecting.
|
13
|
+
* @returns {Server} - The Socket.IO server instance.
|
14
|
+
*/
|
15
|
+
get io(): Server;
|
16
|
+
/**
|
17
|
+
* Connect to the Socket.IO server using the specified HTTP server.
|
18
|
+
*
|
19
|
+
* The server instance will be stored in the `_io` property for later access.
|
20
|
+
*
|
21
|
+
* @param {http.Server} httpServer - The HTTP server instance to attach Socket.IO to.
|
22
|
+
*/
|
7
23
|
connect(httpServer: http.Server): void;
|
8
24
|
}
|
25
|
+
/**
|
26
|
+
* Singleton instance of the SocketWrapper class to provide a centralized Socket.IO server connection.
|
27
|
+
*/
|
9
28
|
export declare const socketWrapper: SocketWrapper;
|
@@ -2,12 +2,28 @@
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
3
|
exports.socketWrapper = exports.SocketWrapper = void 0;
|
4
4
|
const socket_io_1 = require("socket.io");
|
5
|
+
/**
|
6
|
+
* A wrapper class for the Socket.IO server to manage connections and provide a centralized server instance.
|
7
|
+
*/
|
5
8
|
class SocketWrapper {
|
9
|
+
/**
|
10
|
+
* Get the Socket.IO server instance.
|
11
|
+
*
|
12
|
+
* @throws {Error} - Throws an error if the Socket.IO server is accessed before connecting.
|
13
|
+
* @returns {Server} - The Socket.IO server instance.
|
14
|
+
*/
|
6
15
|
get io() {
|
7
16
|
if (!this._io)
|
8
17
|
throw new Error("Cannot access IO Client before connecting");
|
9
18
|
return this._io;
|
10
19
|
}
|
20
|
+
/**
|
21
|
+
* Connect to the Socket.IO server using the specified HTTP server.
|
22
|
+
*
|
23
|
+
* The server instance will be stored in the `_io` property for later access.
|
24
|
+
*
|
25
|
+
* @param {http.Server} httpServer - The HTTP server instance to attach Socket.IO to.
|
26
|
+
*/
|
11
27
|
connect(httpServer) {
|
12
28
|
this._io = new socket_io_1.Server(httpServer, {
|
13
29
|
path: "/socket/",
|
@@ -22,4 +38,7 @@ class SocketWrapper {
|
|
22
38
|
}
|
23
39
|
}
|
24
40
|
exports.SocketWrapper = SocketWrapper;
|
41
|
+
/**
|
42
|
+
* Singleton instance of the SocketWrapper class to provide a centralized Socket.IO server connection.
|
43
|
+
*/
|
25
44
|
exports.socketWrapper = new SocketWrapper();
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { NatsConnection, ConsumerConfig, JsMsg } from "nats";
|
2
|
+
import { DurableName, Subjects, Streams } from "../common/enums";
|
3
|
+
import { MainSubjects, ValidateSubjectFormat } from "../common/enums/subjects/main-subjects";
|
4
|
+
interface Event {
|
5
|
+
subject: ValidateSubjectFormat<Subjects, MainSubjects>;
|
6
|
+
durableName?: DurableName;
|
7
|
+
stream: Streams;
|
8
|
+
data: any;
|
9
|
+
}
|
10
|
+
export declare abstract class NatsConsumer<T extends Event> {
|
11
|
+
protected client: NatsConnection;
|
12
|
+
protected jc: import("nats").Codec<T>;
|
13
|
+
protected sc: import("nats").Codec<string>;
|
14
|
+
abstract stream: T["stream"];
|
15
|
+
abstract subject: T["subject"];
|
16
|
+
abstract durableName: T["durableName"];
|
17
|
+
abstract maxConcurrentMessages: number;
|
18
|
+
protected ackWait: number;
|
19
|
+
constructor(client: NatsConnection);
|
20
|
+
get consumerOptions(): Partial<ConsumerConfig>;
|
21
|
+
consume(): Promise<void>;
|
22
|
+
abstract processMessage(data: T["data"], msg: JsMsg): Promise<void>;
|
23
|
+
protected close(): Promise<void>;
|
24
|
+
protected parsedMessage: (msg: JsMsg) => string | T;
|
25
|
+
}
|
26
|
+
export {};
|
@@ -0,0 +1,96 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9
|
+
});
|
10
|
+
};
|
11
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
12
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
13
|
+
var m = o[Symbol.asyncIterator], i;
|
14
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
15
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
16
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
17
|
+
};
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
19
|
+
exports.NatsConsumer = void 0;
|
20
|
+
const nats_1 = require("nats");
|
21
|
+
class NatsConsumer {
|
22
|
+
constructor(client) {
|
23
|
+
this.jc = (0, nats_1.JSONCodec)();
|
24
|
+
this.sc = (0, nats_1.StringCodec)();
|
25
|
+
this.ackWait = (0, nats_1.nanos)(5 * 1000);
|
26
|
+
this.parsedMessage = (msg) => typeof msg.data === "string" ? this.sc.decode(msg.data) : this.jc.decode(msg.data);
|
27
|
+
this.client = client;
|
28
|
+
}
|
29
|
+
get consumerOptions() {
|
30
|
+
return {
|
31
|
+
deliver_policy: nats_1.DeliverPolicy.All,
|
32
|
+
ack_policy: nats_1.AckPolicy.Explicit,
|
33
|
+
ack_wait: this.ackWait,
|
34
|
+
durable_name: this.durableName,
|
35
|
+
replay_policy: nats_1.ReplayPolicy.Instant,
|
36
|
+
filter_subject: this.subject,
|
37
|
+
};
|
38
|
+
}
|
39
|
+
consume() {
|
40
|
+
var _a, e_1, _b, _c;
|
41
|
+
return __awaiter(this, void 0, void 0, function* () {
|
42
|
+
console.log(this.stream, this.durableName, this.subject);
|
43
|
+
try {
|
44
|
+
const jsm = yield this.client.jetstreamManager();
|
45
|
+
yield jsm.consumers.add(this.stream, this.consumerOptions);
|
46
|
+
const js = this.client.jetstream();
|
47
|
+
const consumer = yield js.consumers.get(this.stream, this.durableName);
|
48
|
+
const messages = yield consumer.consume({ max_messages: 5 });
|
49
|
+
let pendingMessages = [];
|
50
|
+
let inFlight = 0;
|
51
|
+
try {
|
52
|
+
for (var _d = true, messages_1 = __asyncValues(messages), messages_1_1; messages_1_1 = yield messages_1.next(), _a = messages_1_1.done, !_a; _d = true) {
|
53
|
+
_c = messages_1_1.value;
|
54
|
+
_d = false;
|
55
|
+
const msg = _c;
|
56
|
+
// Process messages asynchronously up to the maximum concurrent limit
|
57
|
+
if (inFlight < this.maxConcurrentMessages) {
|
58
|
+
const parsedMessage = this.parsedMessage(msg);
|
59
|
+
inFlight++;
|
60
|
+
yield this.processMessage(parsedMessage, msg).then(() => { inFlight--; }).catch(() => { inFlight--; });
|
61
|
+
}
|
62
|
+
else {
|
63
|
+
console.log("PENDING MESSAGES: ", pendingMessages);
|
64
|
+
pendingMessages.push(msg); // Store the message to be processed later
|
65
|
+
}
|
66
|
+
// Check if there are pending messages that can be processed
|
67
|
+
while (pendingMessages.length && inFlight < this.maxConcurrentMessages) {
|
68
|
+
console.log("PENDING MESSAGES: ", pendingMessages);
|
69
|
+
const pendingMessage = pendingMessages.shift();
|
70
|
+
const parsedMessage = this.parsedMessage(pendingMessage);
|
71
|
+
inFlight++;
|
72
|
+
yield this.processMessage(parsedMessage, pendingMessage).then(() => { inFlight--; }).catch(() => { inFlight--; });
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
77
|
+
finally {
|
78
|
+
try {
|
79
|
+
if (!_d && !_a && (_b = messages_1.return)) yield _b.call(messages_1);
|
80
|
+
}
|
81
|
+
finally { if (e_1) throw e_1.error; }
|
82
|
+
}
|
83
|
+
}
|
84
|
+
catch (error) {
|
85
|
+
console.error(error.message);
|
86
|
+
// throw new Error("Failed to consume messages");
|
87
|
+
}
|
88
|
+
});
|
89
|
+
}
|
90
|
+
close() {
|
91
|
+
return __awaiter(this, void 0, void 0, function* () {
|
92
|
+
yield this.client.drain();
|
93
|
+
});
|
94
|
+
}
|
95
|
+
}
|
96
|
+
exports.NatsConsumer = NatsConsumer;
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { PubAck, NatsConnection } from "nats";
|
2
|
+
import { Subjects } from "../common/enums";
|
3
|
+
import { MainSubjects, ValidateSubjectFormat } from "../common/enums/subjects/main-subjects";
|
4
|
+
interface Event {
|
5
|
+
subject: ValidateSubjectFormat<Subjects, MainSubjects>;
|
6
|
+
data: any;
|
7
|
+
}
|
8
|
+
export declare abstract class NatsPublisher<T extends Event> {
|
9
|
+
private jc;
|
10
|
+
private sc;
|
11
|
+
abstract subject: T["subject"];
|
12
|
+
publish(nc: NatsConnection, data: T["data"]): Promise<PubAck>;
|
13
|
+
}
|
14
|
+
export {};
|
@@ -0,0 +1,32 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9
|
+
});
|
10
|
+
};
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
12
|
+
exports.NatsPublisher = void 0;
|
13
|
+
const nats_1 = require("nats");
|
14
|
+
class NatsPublisher {
|
15
|
+
constructor() {
|
16
|
+
this.jc = (0, nats_1.JSONCodec)();
|
17
|
+
this.sc = (0, nats_1.StringCodec)();
|
18
|
+
}
|
19
|
+
publish(nc, data) {
|
20
|
+
return __awaiter(this, void 0, void 0, function* () {
|
21
|
+
try {
|
22
|
+
const jetStreamClient = nc.jetstream();
|
23
|
+
return yield jetStreamClient.publish(this.subject, typeof data === "string" ? this.sc.encode(data) : this.jc.encode(data));
|
24
|
+
}
|
25
|
+
catch (error) {
|
26
|
+
console.error(error.message);
|
27
|
+
throw new Error(`Failed to publish message. Error: [${error.message}]`);
|
28
|
+
}
|
29
|
+
});
|
30
|
+
}
|
31
|
+
}
|
32
|
+
exports.NatsPublisher = NatsPublisher;
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { NatsConnection } from "nats";
|
2
|
+
import { Streams } from "../common/enums";
|
3
|
+
import { MainSubjects } from "../common/enums/subjects/main-subjects";
|
4
|
+
interface Event {
|
5
|
+
subject: MainSubjects;
|
6
|
+
stream: Streams;
|
7
|
+
}
|
8
|
+
export declare abstract class NatsStream<T extends Event> {
|
9
|
+
abstract subject: T["subject"];
|
10
|
+
abstract stream: T['stream'];
|
11
|
+
init(client: NatsConnection): Promise<void>;
|
12
|
+
}
|
13
|
+
export {};
|
@@ -0,0 +1,28 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9
|
+
});
|
10
|
+
};
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
12
|
+
exports.NatsStream = void 0;
|
13
|
+
class NatsStream {
|
14
|
+
init(client) {
|
15
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16
|
+
try {
|
17
|
+
const jsm = yield client.jetstreamManager();
|
18
|
+
yield jsm.streams.add({ name: this.stream, subjects: [this.subject] });
|
19
|
+
return;
|
20
|
+
}
|
21
|
+
catch (error) {
|
22
|
+
console.error(error.message);
|
23
|
+
throw new Error("Failed to add stream");
|
24
|
+
}
|
25
|
+
});
|
26
|
+
}
|
27
|
+
}
|
28
|
+
exports.NatsStream = NatsStream;
|
@@ -0,0 +1,9 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.OrderDurableName = void 0;
|
4
|
+
var OrderDurableName;
|
5
|
+
(function (OrderDurableName) {
|
6
|
+
OrderDurableName["OrderCancelledDurablename"] = "order-cancelled-consumer";
|
7
|
+
OrderDurableName["OrderRequestDurablename"] = "order-requested-consumer";
|
8
|
+
OrderDurableName["OrderCompletedDurablename"] = "order-completed-consumer";
|
9
|
+
})(OrderDurableName || (exports.OrderDurableName = OrderDurableName = {}));
|
@@ -0,0 +1,8 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.PaymentDurableName = void 0;
|
4
|
+
var PaymentDurableName;
|
5
|
+
(function (PaymentDurableName) {
|
6
|
+
PaymentDurableName["PaymentFailedDurableName"] = "payment-failed-consumer";
|
7
|
+
PaymentDurableName["PaymentCreatedDurableName"] = "payment-created-consumer";
|
8
|
+
})(PaymentDurableName || (exports.PaymentDurableName = PaymentDurableName = {}));
|
@@ -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("./durable-names"), exports);
|
18
|
+
__exportStar(require("./streams"), exports);
|
19
|
+
__exportStar(require("./subjects"), exports);
|
@@ -0,0 +1,7 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.PaymentStream = void 0;
|
4
|
+
var PaymentStream;
|
5
|
+
(function (PaymentStream) {
|
6
|
+
PaymentStream["Payment"] = "PAYMENT";
|
7
|
+
})(PaymentStream || (exports.PaymentStream = PaymentStream = {}));
|
@@ -0,0 +1,10 @@
|
|
1
|
+
export declare enum MainSubjects {
|
2
|
+
ORDER = "order.*",
|
3
|
+
PAYMENT = "payment.*",
|
4
|
+
ETA = "eta.*"
|
5
|
+
}
|
6
|
+
type SubjectFormat<T extends string, U extends string> = `${T}.${U}`;
|
7
|
+
type ExtractStringBeforeColon<T extends string> = T extends `${infer StringBeforeColon}.*` ? StringBeforeColon : never;
|
8
|
+
export type ValidateSubjectFormat<T extends string, U extends string> = U extends MainSubjects ? T extends SubjectFormat<ExtractStringBeforeColon<MainSubjects>, string> ? T : never : never;
|
9
|
+
export declare const spreadEnumValues: <T extends Record<string, string>>(enumObject: T) => string[];
|
10
|
+
export {};
|
@@ -0,0 +1,11 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.spreadEnumValues = exports.MainSubjects = void 0;
|
4
|
+
var MainSubjects;
|
5
|
+
(function (MainSubjects) {
|
6
|
+
MainSubjects["ORDER"] = "order.*";
|
7
|
+
MainSubjects["PAYMENT"] = "payment.*";
|
8
|
+
MainSubjects["ETA"] = "eta.*";
|
9
|
+
})(MainSubjects || (exports.MainSubjects = MainSubjects = {}));
|
10
|
+
const spreadEnumValues = (enumObject) => Object.values(enumObject);
|
11
|
+
exports.spreadEnumValues = spreadEnumValues;
|
@@ -0,0 +1,9 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.OrderSubjects = void 0;
|
4
|
+
var OrderSubjects;
|
5
|
+
(function (OrderSubjects) {
|
6
|
+
OrderSubjects["OrderCancelled"] = "order.cancelled";
|
7
|
+
OrderSubjects["OrderRequested"] = "order.requested";
|
8
|
+
OrderSubjects["OrderCompleted"] = "order.completed";
|
9
|
+
})(OrderSubjects || (exports.OrderSubjects = OrderSubjects = {}));
|
@@ -0,0 +1,8 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.PaymentSubjects = void 0;
|
4
|
+
var PaymentSubjects;
|
5
|
+
(function (PaymentSubjects) {
|
6
|
+
PaymentSubjects["PaymentCreated"] = "payment.created";
|
7
|
+
PaymentSubjects["PaymentFailed"] = "payment.failed";
|
8
|
+
})(PaymentSubjects || (exports.PaymentSubjects = PaymentSubjects = {}));
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { OrderDurableName } from '../../enums/durable-names/order-dname';
|
2
|
+
import { OrderStream } from '../../enums/streams/order-stream';
|
3
|
+
import { OrderSubjects } from '../../enums/subjects/order-subjects';
|
4
|
+
export interface OrderCancelledEvent {
|
5
|
+
subject: OrderSubjects.OrderCancelled;
|
6
|
+
durableName?: OrderDurableName.OrderCancelledDurablename;
|
7
|
+
stream: OrderStream.Order;
|
8
|
+
data: {
|
9
|
+
id: string;
|
10
|
+
version: number;
|
11
|
+
status: string;
|
12
|
+
userid: string;
|
13
|
+
tripid: string;
|
14
|
+
};
|
15
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { OrderDurableName } from '../../enums/durable-names/order-dname';
|
2
|
+
import { OrderStream } from '../../enums/streams/order-stream';
|
3
|
+
import { OrderSubjects } from '../../enums/subjects/order-subjects';
|
4
|
+
export interface OrderCompletedEvent {
|
5
|
+
subject: OrderSubjects.OrderCompleted;
|
6
|
+
durableName?: OrderDurableName.OrderCompletedDurablename;
|
7
|
+
stream: OrderStream.Order;
|
8
|
+
data: {
|
9
|
+
id: string;
|
10
|
+
version: number;
|
11
|
+
status: string;
|
12
|
+
userid: string;
|
13
|
+
tripid: string;
|
14
|
+
};
|
15
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { OrderDurableName } from '../../enums/durable-names/order-dname';
|
2
|
+
import { OrderStream } from '../../enums/streams/order-stream';
|
3
|
+
import { OrderSubjects } from '../../enums/subjects/order-subjects';
|
4
|
+
export interface OrderRequestedEvent {
|
5
|
+
subject: OrderSubjects.OrderRequested;
|
6
|
+
durableName?: OrderDurableName.OrderRequestDurablename;
|
7
|
+
stream: OrderStream.Order;
|
8
|
+
data: {
|
9
|
+
id: string;
|
10
|
+
version: number;
|
11
|
+
status: string;
|
12
|
+
userid: string;
|
13
|
+
tripid: string;
|
14
|
+
};
|
15
|
+
}
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
"use strict";
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
"use strict";
|
@@ -1,2 +1,11 @@
|
|
1
1
|
import { Request, Response, NextFunction } from "express";
|
2
|
+
/**
|
3
|
+
* Middleware function to extract the current user from the request's Authorization header.
|
4
|
+
*
|
5
|
+
* @param {Request} req - The Express Request object.
|
6
|
+
* @param {Response} res - The Express Response object.
|
7
|
+
* @param {NextFunction} next - The next function in the middleware chain.
|
8
|
+
*
|
9
|
+
* @returns {Promise<void>} - A Promise that resolves once the user extraction is complete, but it is not used directly.
|
10
|
+
*/
|
2
11
|
export declare const currentUser: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
@@ -34,26 +34,44 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
35
35
|
exports.currentUser = void 0;
|
36
36
|
const jsonwebtoken_1 = __importStar(require("jsonwebtoken"));
|
37
|
+
/**
|
38
|
+
* Middleware function to extract the current user from the request's Authorization header.
|
39
|
+
*
|
40
|
+
* @param {Request} req - The Express Request object.
|
41
|
+
* @param {Response} res - The Express Response object.
|
42
|
+
* @param {NextFunction} next - The next function in the middleware chain.
|
43
|
+
*
|
44
|
+
* @returns {Promise<void>} - A Promise that resolves once the user extraction is complete, but it is not used directly.
|
45
|
+
*/
|
37
46
|
const currentUser = (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
|
38
47
|
let result = null;
|
39
48
|
try {
|
49
|
+
// Extract the Bearer token from the Authorization header.
|
40
50
|
const bearerToken = req.headers["authorization"];
|
41
51
|
if (typeof bearerToken !== "undefined" && bearerToken) {
|
42
52
|
const token = bearerToken.split(" ")[1];
|
43
53
|
if (token) {
|
54
|
+
// Verify the JWT token and decode the user payload.
|
44
55
|
result = jsonwebtoken_1.default.verify(token, process.env.JWT_KEY.split("-").join(""));
|
45
56
|
}
|
46
57
|
}
|
58
|
+
// Attach the user payload to the request object for later use in the application.
|
47
59
|
req.currentUser = result;
|
48
60
|
}
|
49
61
|
catch (err) {
|
62
|
+
// Handle errors related to token verification or expiration.
|
50
63
|
if (err instanceof jsonwebtoken_1.TokenExpiredError) {
|
64
|
+
// If the token is expired, you may consider refreshing the token or taking appropriate actions.
|
65
|
+
// Uncomment the following line if you want to handle token expiration using the AuthController.
|
66
|
+
// await AuthController.refreshToken(req, res);
|
51
67
|
// await AuthController.refreshToken(req, res)
|
52
68
|
}
|
53
69
|
else {
|
70
|
+
// If any other error occurs during token verification, set currentUser to null.
|
54
71
|
req.currentUser = null;
|
55
72
|
}
|
56
73
|
}
|
74
|
+
// Call the next middleware in the chain.
|
57
75
|
next();
|
58
76
|
});
|
59
77
|
exports.currentUser = currentUser;
|
@@ -1,2 +1,10 @@
|
|
1
1
|
import { NextFunction, Request, Response } from "express";
|
2
|
+
/**
|
3
|
+
* Express error handling middleware to handle custom errors and provide appropriate responses.
|
4
|
+
*
|
5
|
+
* @param {Error} err - The error object that needs to be handled.
|
6
|
+
* @param {Request} req - The Express Request object.
|
7
|
+
* @param {Response} res - The Express Response object.
|
8
|
+
* @param {NextFunction} next - The next function in the middleware chain.
|
9
|
+
*/
|
2
10
|
export declare const errorHandler: (err: Error, req: Request, res: Response, next: NextFunction) => Response<any, Record<string, any>> | undefined;
|
@@ -3,6 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.errorHandler = void 0;
|
4
4
|
const custom_error_1 = require("../errors/custom-error");
|
5
5
|
const enums_1 = require("../utils/enums");
|
6
|
+
/**
|
7
|
+
* Express error handling middleware to handle custom errors and provide appropriate responses.
|
8
|
+
*
|
9
|
+
* @param {Error} err - The error object that needs to be handled.
|
10
|
+
* @param {Request} req - The Express Request object.
|
11
|
+
* @param {Response} res - The Express Response object.
|
12
|
+
* @param {NextFunction} next - The next function in the middleware chain.
|
13
|
+
*/
|
6
14
|
const errorHandler = (err, req, res, next) => {
|
7
15
|
if (err instanceof custom_error_1.CustomError) {
|
8
16
|
return res.status(err.statusCode).send({ errors: err.serializeErrors() });
|
@@ -1,7 +1,23 @@
|
|
1
1
|
import { Queue } from "bullmq";
|
2
|
+
/**
|
3
|
+
* Abstract class representing a Job Queue.
|
4
|
+
*/
|
2
5
|
export declare abstract class AbstractJobQueue {
|
3
6
|
protected que: Queue;
|
4
7
|
protected abstract jobName: string;
|
8
|
+
/**
|
9
|
+
* Creates an instance of AbstractJobQueue.
|
10
|
+
*
|
11
|
+
* @param {string} queueName - The name of the job queue.
|
12
|
+
*/
|
5
13
|
constructor(queueName: string);
|
14
|
+
/**
|
15
|
+
* Abstract method to add jobs to the job queue.
|
16
|
+
*
|
17
|
+
* This method should be implemented by subclasses to add specific jobs to the queue.
|
18
|
+
*
|
19
|
+
* @param {any} data - The data representing the job to be added to the queue.
|
20
|
+
* @returns {Promise<void>} - A Promise that resolves when the job is added to the queue successfully.
|
21
|
+
*/
|
6
22
|
abstract addJobs(data: any): Promise<void>;
|
7
23
|
}
|
@@ -3,9 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AbstractJobQueue = void 0;
|
4
4
|
const bullmq_1 = require("bullmq");
|
5
5
|
const connections_wrappers_1 = require("../../../connections-wrappers");
|
6
|
-
|
6
|
+
/**
|
7
|
+
* Abstract class representing a Job Queue.
|
8
|
+
*/
|
7
9
|
class AbstractJobQueue {
|
10
|
+
/**
|
11
|
+
* Creates an instance of AbstractJobQueue.
|
12
|
+
*
|
13
|
+
* @param {string} queueName - The name of the job queue.
|
14
|
+
*/
|
8
15
|
constructor(queueName) {
|
16
|
+
// Initialize the job queue with the provided name and a Redis connection from the redisWrapper.
|
9
17
|
this.que = new bullmq_1.Queue(queueName, { connection: connections_wrappers_1.redisWrapper.client });
|
10
18
|
}
|
11
19
|
}
|
@@ -1,6 +1,22 @@
|
|
1
1
|
import { Job, Worker } from "bullmq";
|
2
|
+
/**
|
3
|
+
* Abstract class representing a worker for processing jobs from a Job Queue.
|
4
|
+
*/
|
2
5
|
export declare abstract class AbstractWorker {
|
3
6
|
protected worker: Worker;
|
7
|
+
/**
|
8
|
+
* Creates an instance of AbstractWorker.
|
9
|
+
*
|
10
|
+
* @param {string} queueName - The name of the job queue that this worker will process jobs from.
|
11
|
+
*/
|
4
12
|
constructor(queueName: string);
|
13
|
+
/**
|
14
|
+
* Abstract method to process jobs retrieved from the job queue.
|
15
|
+
*
|
16
|
+
* This method should be implemented by subclasses to handle specific job processing logic.
|
17
|
+
*
|
18
|
+
* @param {Job} job - The job object representing the job to be processed.
|
19
|
+
* @returns {Promise<void>} - A Promise that resolves when the job processing is completed successfully.
|
20
|
+
*/
|
5
21
|
protected abstract process(job: Job): Promise<void>;
|
6
22
|
}
|
@@ -3,15 +3,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AbstractWorker = void 0;
|
4
4
|
const bullmq_1 = require("bullmq");
|
5
5
|
const connections_wrappers_1 = require("../../../connections-wrappers");
|
6
|
+
/**
|
7
|
+
* Abstract class representing a worker for processing jobs from a Job Queue.
|
8
|
+
*/
|
6
9
|
class AbstractWorker {
|
10
|
+
/**
|
11
|
+
* Creates an instance of AbstractWorker.
|
12
|
+
*
|
13
|
+
* @param {string} queueName - The name of the job queue that this worker will process jobs from.
|
14
|
+
*/
|
7
15
|
constructor(queueName) {
|
16
|
+
// Create a worker instance with the provided queueName and bind the 'process' method to this worker.
|
8
17
|
this.worker = new bullmq_1.Worker(queueName, this.process.bind(this), {
|
9
18
|
connection: connections_wrappers_1.redisWrapper.client,
|
10
19
|
autorun: false,
|
11
20
|
});
|
21
|
+
// Handle errors that may occur during job processing.
|
12
22
|
this.worker.on("error", (err) => {
|
13
23
|
console.error(`${queueName} Error: `, err);
|
14
24
|
});
|
25
|
+
// Start the worker in non-autorun mode, which means it won't start processing jobs automatically.
|
15
26
|
console.log(" --- WORKER STARTED --- ", queueName);
|
16
27
|
this.worker.run();
|
17
28
|
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
/**
|
2
|
+
* Generates random codes as an async function using await.
|
3
|
+
*
|
4
|
+
* @param {string} characters - The set of characters to use for generating codes.
|
5
|
+
* @param {number} numberOfCodes - The total number of codes to generate.
|
6
|
+
* @param {number} codeSize - The size of each individual code (number of characters).
|
7
|
+
* @param {boolean} includeHyphens - Whether to include hyphens between codes.
|
8
|
+
* @param {number} chunkSize - The number of codes to generate in each chunk (iteration).
|
9
|
+
*
|
10
|
+
* @returns {Promise<string>} - A Promise that resolves with the generated codes as a string.
|
11
|
+
*/
|
12
|
+
declare function generateRandomCodesAsync(characters: string, numberOfCodes: number, codeSize: number, includeHyphens: boolean, chunkSize: number): Promise<string>;
|
@@ -0,0 +1,52 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9
|
+
});
|
10
|
+
};
|
11
|
+
/**
|
12
|
+
* Generates random codes as an async function using await.
|
13
|
+
*
|
14
|
+
* @param {string} characters - The set of characters to use for generating codes.
|
15
|
+
* @param {number} numberOfCodes - The total number of codes to generate.
|
16
|
+
* @param {number} codeSize - The size of each individual code (number of characters).
|
17
|
+
* @param {boolean} includeHyphens - Whether to include hyphens between codes.
|
18
|
+
* @param {number} chunkSize - The number of codes to generate in each chunk (iteration).
|
19
|
+
*
|
20
|
+
* @returns {Promise<string>} - A Promise that resolves with the generated codes as a string.
|
21
|
+
*/
|
22
|
+
function generateRandomCodesAsync(characters, numberOfCodes, codeSize, includeHyphens, chunkSize) {
|
23
|
+
return __awaiter(this, void 0, void 0, function* () {
|
24
|
+
let codes = "";
|
25
|
+
let hyphen = includeHyphens ? "-" : "";
|
26
|
+
let chunkStart = 0;
|
27
|
+
let generatedCodes = new Set();
|
28
|
+
function generateChunk() {
|
29
|
+
return __awaiter(this, void 0, void 0, function* () {
|
30
|
+
for (let i = chunkStart; i < chunkStart + chunkSize && i < numberOfCodes; i++) {
|
31
|
+
let code = "";
|
32
|
+
for (let j = 0; j < codeSize; j++) {
|
33
|
+
code += characters.charAt(Math.floor(Math.random() * characters.length));
|
34
|
+
}
|
35
|
+
if (!generatedCodes.has(code)) {
|
36
|
+
generatedCodes.add(code);
|
37
|
+
codes += code + (i === numberOfCodes - 1 ? "" : hyphen);
|
38
|
+
}
|
39
|
+
else {
|
40
|
+
i--;
|
41
|
+
}
|
42
|
+
}
|
43
|
+
chunkStart += chunkSize;
|
44
|
+
if (chunkStart < numberOfCodes && generatedCodes.size < numberOfCodes) {
|
45
|
+
yield generateChunk();
|
46
|
+
}
|
47
|
+
});
|
48
|
+
}
|
49
|
+
yield generateChunk();
|
50
|
+
return codes;
|
51
|
+
});
|
52
|
+
}
|
package/build/utils/read-dir.js
CHANGED
@@ -39,13 +39,3 @@ function readDirectoryRecursive(dirPath, filter) {
|
|
39
39
|
});
|
40
40
|
}
|
41
41
|
exports.readDirectoryRecursive = readDirectoryRecursive;
|
42
|
-
// // Example usage:
|
43
|
-
// const directoryPath = '/path/to/your/directory';
|
44
|
-
// (async () => {
|
45
|
-
// try {
|
46
|
-
// const filteredFiles = await readDirectoryRecursive(directoryPath, (filePath) => filePath.endsWith('.txt'));
|
47
|
-
// console.log(filteredFiles);
|
48
|
-
// } catch (err) {
|
49
|
-
// console.error('Error occurred:', err);
|
50
|
-
// }
|
51
|
-
// })();
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@hastehaul/common",
|
3
|
-
"version": "1.0.
|
3
|
+
"version": "1.0.13",
|
4
4
|
"description": "",
|
5
5
|
"main": "./build/index.js",
|
6
6
|
"types": "./build/index.d.ts",
|
@@ -27,6 +27,7 @@
|
|
27
27
|
"express": "^4.18.2",
|
28
28
|
"ioredis": "^5.3.2",
|
29
29
|
"jsonwebtoken": "^9.0.1",
|
30
|
+
"nats": "^2.15.1",
|
30
31
|
"socket.io": "^4.7.1"
|
31
32
|
}
|
32
33
|
}
|