@vortenixinnovations/flagra-common-module 1.0.56 → 1.0.60

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.
Files changed (35) hide show
  1. package/build/events/account/account-created-event.d.ts +11 -0
  2. package/build/events/account/account-created-event.js +1 -0
  3. package/build/events/account/account-verify-event.d.ts +10 -0
  4. package/build/events/account/account-verify-event.js +1 -0
  5. package/build/events/base/base-event.d.ts +5 -0
  6. package/build/events/base/base-event.js +1 -0
  7. package/build/events/base/base-listener.d.ts +21 -0
  8. package/build/events/base/base-listener.js +75 -0
  9. package/build/events/base/base-publisher.d.ts +9 -0
  10. package/build/events/base/base-publisher.js +15 -0
  11. package/build/events/base/base-request-listener.d.ts +11 -0
  12. package/build/events/base/base-request-listener.js +25 -0
  13. package/build/events/base/base-request-publisher.d.ts +9 -0
  14. package/build/events/base/base-request-publisher.js +22 -0
  15. package/build/events/base/request-event.d.ts +6 -0
  16. package/build/events/base/request-event.js +1 -0
  17. package/build/events/base/subjects.d.ts +4 -0
  18. package/build/events/base/subjects.js +5 -0
  19. package/build/index.d.ts +8 -0
  20. package/build/index.js +8 -0
  21. package/build/util/index.d.ts +0 -3
  22. package/build/util/index.js +0 -3
  23. package/package.json +20 -32
  24. package/build/util/error/errors.d.ts +0 -19
  25. package/build/util/error/errors.js +0 -38
  26. package/build/util/error/handler.d.ts +0 -3
  27. package/build/util/error/handler.js +0 -33
  28. package/build/util/error/index.d.ts +0 -3
  29. package/build/util/error/index.js +0 -3
  30. package/build/util/error/status-codes.d.ts +0 -7
  31. package/build/util/error/status-codes.js +0 -7
  32. package/build/util/error/validator.d.ts +0 -1
  33. package/build/util/error/validator.js +0 -14
  34. package/build/util/validator.d.ts +0 -9
  35. package/build/util/validator.js +0 -43
@@ -0,0 +1,11 @@
1
+ import { Subjects } from "../base/subjects.js";
2
+ export interface AccountCreatedEvent {
3
+ subject: Subjects.AccountCreated;
4
+ data: {
5
+ id: string;
6
+ fullname: string;
7
+ role: string;
8
+ email: string;
9
+ password: string;
10
+ };
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import { Subjects } from "../base/subjects.js";
2
+ export interface AccountVerifyEvent {
3
+ subject: Subjects.AccountVerify;
4
+ data: {
5
+ id: string;
6
+ first: string;
7
+ email: string;
8
+ otp: number;
9
+ };
10
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { Subjects } from "./subjects.js";
2
+ export interface Event {
3
+ subject: Subjects;
4
+ data: any;
5
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ import { JetStreamClient, JetStreamManager, JsMsg, NatsConnection, ConsumerConfig } from "nats";
2
+ import { Subjects } from "./subjects.js";
3
+ interface Event {
4
+ subject: Subjects;
5
+ data: any;
6
+ }
7
+ export declare abstract class Listener<T extends Event> {
8
+ protected nc: NatsConnection;
9
+ abstract subject: T["subject"];
10
+ abstract queueGroupName: string;
11
+ abstract onMessage(data: T["data"], msg: JsMsg): void;
12
+ protected client: JetStreamClient;
13
+ protected manager: JetStreamManager;
14
+ protected ackWait: number;
15
+ constructor(nc: NatsConnection);
16
+ init(): Promise<void>;
17
+ subscriptionOptions(): Partial<ConsumerConfig>;
18
+ listen(): Promise<void>;
19
+ private parseMessage;
20
+ }
21
+ export {};
@@ -0,0 +1,75 @@
1
+ import { AckPolicy, StorageType, DeliverPolicy, } from "nats";
2
+ import { logger } from "../../util/logger/index.js";
3
+ export class Listener {
4
+ nc;
5
+ client;
6
+ manager;
7
+ ackWait = 30_000_000_000; // 30 seconds in nanoseconds
8
+ constructor(nc) {
9
+ this.nc = nc;
10
+ this.client = nc.jetstream();
11
+ }
12
+ async init() {
13
+ this.manager = await this.nc.jetstreamManager();
14
+ }
15
+ subscriptionOptions() {
16
+ return {
17
+ durable_name: this.queueGroupName,
18
+ ack_policy: AckPolicy.Explicit,
19
+ deliver_policy: DeliverPolicy.All,
20
+ ack_wait: this.ackWait,
21
+ max_ack_pending: 100,
22
+ max_deliver: 5,
23
+ };
24
+ }
25
+ async listen() {
26
+ if (!this.manager)
27
+ await this.init();
28
+ const streamName = `STREAM_${this.subject}`;
29
+ const durableName = this.queueGroupName;
30
+ // Ensure the stream exists
31
+ try {
32
+ await this.manager.streams.info(streamName);
33
+ }
34
+ catch {
35
+ logger.info(`Creating stream: ${streamName}`);
36
+ await this.manager.streams.add({
37
+ name: streamName,
38
+ subjects: [this.subject],
39
+ storage: StorageType.File,
40
+ });
41
+ }
42
+ // Ensure the consumer (durable queue group) exists
43
+ try {
44
+ await this.manager.consumers.info(streamName, durableName);
45
+ }
46
+ catch {
47
+ logger.info(`Creating durable consumer: ${durableName}`);
48
+ await this.manager.consumers.add(streamName, this.subscriptionOptions());
49
+ }
50
+ logger.info(`Listening for subject "${this.subject}" with queue group "${durableName}"`);
51
+ const consumer = await this.client.consumers.get(streamName, durableName);
52
+ // Use next() in a loop - simplest and works reliably
53
+ while (true) {
54
+ try {
55
+ const msg = await consumer.next({ expires: 30000 });
56
+ if (msg) {
57
+ const parsedData = this.parseMessage(msg);
58
+ this.onMessage(parsedData, msg);
59
+ }
60
+ }
61
+ catch (err) {
62
+ // Handle timeout or other errors
63
+ if (err.code !== "408") {
64
+ // 408 is timeout, which is normal
65
+ logger.info("Error getting message:", err);
66
+ await new Promise((resolve) => setTimeout(resolve, 1000));
67
+ }
68
+ }
69
+ }
70
+ }
71
+ parseMessage(msg) {
72
+ const data = msg.data;
73
+ return JSON.parse(new TextDecoder().decode(data));
74
+ }
75
+ }
@@ -0,0 +1,9 @@
1
+ import { NatsConnection } from "nats";
2
+ import { Event } from "./base-event.js";
3
+ export declare abstract class Publisher<T extends Event> {
4
+ abstract subject: T["subject"];
5
+ protected client: NatsConnection;
6
+ private sc;
7
+ constructor(client: NatsConnection);
8
+ publish(data: T["data"]): Promise<void>;
9
+ }
@@ -0,0 +1,15 @@
1
+ import { StringCodec } from "nats";
2
+ import { logger } from "../../util/logger/index.js";
3
+ export class Publisher {
4
+ client;
5
+ sc = StringCodec();
6
+ constructor(client) {
7
+ this.client = client;
8
+ }
9
+ async publish(data) {
10
+ const encoded = this.sc.encode(JSON.stringify(data));
11
+ this.client.publish(this.subject, encoded);
12
+ await this.client.flush();
13
+ logger.info(`Event published to [${this.subject}]: ${JSON.stringify(data)}`);
14
+ }
15
+ }
@@ -0,0 +1,11 @@
1
+ import { NatsConnection } from "nats";
2
+ import { RequestEvent } from "./request-event.js";
3
+ export declare abstract class RequestListener<T extends RequestEvent> {
4
+ abstract subject: T["subject"];
5
+ abstract queueGroupName: string;
6
+ protected client: NatsConnection;
7
+ private sc;
8
+ constructor(client: NatsConnection);
9
+ abstract onRequest(data: T["request"]): Promise<T["response"]>;
10
+ listen(): Promise<void>;
11
+ }
@@ -0,0 +1,25 @@
1
+ import { StringCodec } from "nats";
2
+ import { logger } from "../../util/logger/index.js";
3
+ export class RequestListener {
4
+ client;
5
+ sc = StringCodec();
6
+ constructor(client) {
7
+ this.client = client;
8
+ }
9
+ async listen() {
10
+ const opts = { queue: this.queueGroupName };
11
+ const sub = this.client.subscribe(this.subject, opts);
12
+ logger.info(`Listening for RPC requests on subject [${this.subject}] in queue [${this.queueGroupName}]`);
13
+ for await (const msg of sub) {
14
+ try {
15
+ const data = JSON.parse(this.sc.decode(msg.data));
16
+ const response = await this.onRequest(data);
17
+ msg.respond(this.sc.encode(JSON.stringify(response)));
18
+ }
19
+ catch (err) {
20
+ logger.error(`Failed to process RPC [${this.subject}]: ${err.message}`);
21
+ msg.respond(this.sc.encode(JSON.stringify({ error: err.message })));
22
+ }
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,9 @@
1
+ import { NatsConnection } from "nats";
2
+ import { RequestEvent } from "./request-event.js";
3
+ export declare abstract class RequestPublisher<T extends RequestEvent> {
4
+ abstract subject: T["subject"];
5
+ protected client: NatsConnection;
6
+ private sc;
7
+ constructor(client: NatsConnection);
8
+ send(data: T["request"], timeout?: number): Promise<T["response"]>;
9
+ }
@@ -0,0 +1,22 @@
1
+ import { StringCodec } from "nats";
2
+ import { logger } from "../../util/logger/index.js";
3
+ export class RequestPublisher {
4
+ client;
5
+ sc = StringCodec();
6
+ constructor(client) {
7
+ this.client = client;
8
+ }
9
+ async send(data, timeout = 5000) {
10
+ const encoded = this.sc.encode(JSON.stringify(data));
11
+ try {
12
+ const msg = await this.client.request(this.subject, encoded, { timeout });
13
+ const response = JSON.parse(this.sc.decode(msg.data));
14
+ logger.info(`RPC response received for [${this.subject}]`);
15
+ return response;
16
+ }
17
+ catch (err) {
18
+ logger.error(`RPC request to [${this.subject}] failed: ${err.message}`);
19
+ throw err;
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,6 @@
1
+ import { Subjects } from "./subjects.js";
2
+ export interface RequestEvent {
3
+ subject: Subjects;
4
+ request: any;
5
+ response: any;
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export declare enum Subjects {
2
+ AccountVerify = "account:verify",
3
+ AccountCreated = "account:created"
4
+ }
@@ -0,0 +1,5 @@
1
+ export var Subjects;
2
+ (function (Subjects) {
3
+ Subjects["AccountVerify"] = "account:verify";
4
+ Subjects["AccountCreated"] = "account:created";
5
+ })(Subjects || (Subjects = {}));
package/build/index.d.ts CHANGED
@@ -1 +1,9 @@
1
1
  export * from "./util/index.js";
2
+ export * from "./events/base/base-event.js";
3
+ export * from "./events/base/base-listener.js";
4
+ export * from "./events/base/base-publisher.js";
5
+ export * from "./events/base/subjects.js";
6
+ export * from "./events/account/account-created-event.js";
7
+ export * from "./events/base/request-event.js";
8
+ export * from "./events/base/base-request-publisher.js";
9
+ export * from "./events/base/base-request-listener.js";
package/build/index.js CHANGED
@@ -1 +1,9 @@
1
1
  export * from "./util/index.js";
2
+ export * from "./events/base/base-event.js";
3
+ export * from "./events/base/base-listener.js";
4
+ export * from "./events/base/base-publisher.js";
5
+ export * from "./events/base/subjects.js";
6
+ export * from "./events/account/account-created-event.js";
7
+ export * from "./events/base/request-event.js";
8
+ export * from "./events/base/base-request-publisher.js";
9
+ export * from "./events/base/base-request-listener.js";
@@ -1,4 +1 @@
1
- export * from "./logger/index.js";
2
- export * from "./error/index.js";
3
1
  export * from "./global/index.js";
4
- export * from "./validator.js";
@@ -1,4 +1 @@
1
- export * from "./logger/index.js";
2
- export * from "./error/index.js";
3
1
  export * from "./global/index.js";
4
- export * from "./validator.js";
package/package.json CHANGED
@@ -1,47 +1,35 @@
1
1
  {
2
2
  "name": "@vortenixinnovations/flagra-common-module",
3
- "version": "1.0.56",
4
- "description": "common module of flagra",
3
+ "version": "1.0.60",
4
+ "description": "flagra.com common module",
5
+ "type": "module",
5
6
  "main": "./build/index.js",
6
7
  "types": "./build/index.d.ts",
7
8
  "files": [
8
- "/build/**/*"
9
+ "build/**/*"
9
10
  ],
10
- "publishConfig": {
11
- "access": "public"
12
- },
13
- "exports": {
14
- ".": {
15
- "types": "./build/index.d.ts",
16
- "import": "./build/index.js",
17
- "require": "./build/index.js",
18
- "default": "./build/index.js"
19
- },
20
- "./package.json": "./package.json"
21
- },
22
11
  "scripts": {
23
- "clean": "del-cli ./build/*",
12
+ "clean": "del ./build/*",
24
13
  "build": "npm run clean && tsc",
25
14
  "release": "npm version patch && npm run build && npm publish"
26
15
  },
16
+ "keywords": [],
17
+ "author": "midhunx01",
18
+ "license": "ISC",
19
+ "devDependencies": {
20
+ "@types/node": "^24.9.1",
21
+ "del-cli": "^5.1.0",
22
+ "typescript": "^5.6.2"
23
+ },
27
24
  "dependencies": {
28
- "@sinclair/typebox": "^0.34.41",
29
- "ajv": "^8.17.1",
30
- "ajv-errors": "^3.0.0",
31
- "ajv-formats": "^3.0.1",
32
- "class-validator": "^0.14.2",
25
+ "@types/express": "^5.0.5",
33
26
  "dotenv": "^17.2.3",
34
27
  "express": "^5.1.0",
35
- "pino": "^10.0.0",
28
+ "nats": "^2.29.3",
29
+ "node": "^22.21.0",
30
+ "node-nats-streaming": "^0.3.2",
31
+ "pino": "^10.1.0",
36
32
  "pino-http": "^11.0.0",
37
- "pino-pretty": "^13.0.0"
38
- },
39
- "devDependencies": {
40
- "@types/express": "^5.0.3",
41
- "@types/node": "^24.7.0",
42
- "del-cli": "^7.0.0",
43
- "typescript": "^5.9.3"
44
- },
45
- "author": "midhunx01",
46
- "license": "ISC"
33
+ "pino-pretty": "^13.1.2"
34
+ }
47
35
  }
@@ -1,19 +0,0 @@
1
- declare class BaseError extends Error {
2
- readonly name: string;
3
- readonly status: number;
4
- readonly message: string;
5
- constructor(name: string, status: number, description: string);
6
- }
7
- export declare class APIError extends BaseError {
8
- constructor(description?: string);
9
- }
10
- export declare class ValidationError extends BaseError {
11
- constructor(description?: string);
12
- }
13
- export declare class AuthorizeError extends BaseError {
14
- constructor(description?: string);
15
- }
16
- export declare class NotFoundError extends BaseError {
17
- constructor(description?: string);
18
- }
19
- export {};
@@ -1,38 +0,0 @@
1
- import { STATUS_CODES } from "./status-codes.js";
2
- class BaseError extends Error {
3
- name;
4
- status;
5
- message;
6
- constructor(name, status, description) {
7
- super(description);
8
- this.name = name;
9
- this.status = status;
10
- this.message = description;
11
- Object.setPrototypeOf(this, new.target.prototype);
12
- Error.captureStackTrace(this);
13
- }
14
- }
15
- // 500 Internal Error
16
- export class APIError extends BaseError {
17
- constructor(description = "api error") {
18
- super("api internal server error", STATUS_CODES.INTERNAL_ERROR, description);
19
- }
20
- }
21
- // 400 Validation Error
22
- export class ValidationError extends BaseError {
23
- constructor(description = "bad request") {
24
- super("bad request", STATUS_CODES.BAD_REQUEST, description);
25
- }
26
- }
27
- // 403 Authorize error
28
- export class AuthorizeError extends BaseError {
29
- constructor(description = "access denied") {
30
- super("access denied", STATUS_CODES.UN_AUTHORISED, description);
31
- }
32
- }
33
- // 404 Not Found
34
- export class NotFoundError extends BaseError {
35
- constructor(description = "not found") {
36
- super(description, STATUS_CODES.NOT_FOUND, description);
37
- }
38
- }
@@ -1,3 +0,0 @@
1
- import type { Request, Response, NextFunction } from "express";
2
- export declare const HandleErrorWithLogger: (error: any, req: Request, res: Response, next: NextFunction) => void;
3
- export declare const HandleUnCaughtException: (error: Error) => Promise<never>;
@@ -1,33 +0,0 @@
1
- import { AuthorizeError, NotFoundError, ValidationError } from "./errors.js";
2
- import { logger } from "../logger/index.js";
3
- export const HandleErrorWithLogger = (error, req, res, next) => {
4
- let reportError = true;
5
- let status = 500;
6
- let data = error.message;
7
- // convert express-jwt UnauthorizedError to your AuthorizeError
8
- if (error.name === "UnauthorizedError") {
9
- error = new AuthorizeError(error.message);
10
- }
11
- // skip known errors
12
- [NotFoundError, ValidationError, AuthorizeError].forEach((typeOfError) => {
13
- if (error instanceof typeOfError) {
14
- reportError = false;
15
- status = error.status || 400;
16
- data = error.message;
17
- }
18
- });
19
- if (reportError) {
20
- logger.error(error);
21
- }
22
- else {
23
- // future use third party error reporting tools
24
- logger.warn(error);
25
- }
26
- res.status(status).json({ error: data });
27
- };
28
- export const HandleUnCaughtException = async (error) => {
29
- // error report / monitoring tools
30
- logger.error(error);
31
- // recover
32
- process.exit(1);
33
- };
@@ -1,3 +0,0 @@
1
- export * from "./errors.js";
2
- export * from "./status-codes.js";
3
- export * from "./handler.js";
@@ -1,3 +0,0 @@
1
- export * from "./errors.js";
2
- export * from "./status-codes.js";
3
- export * from "./handler.js";
@@ -1,7 +0,0 @@
1
- export declare const STATUS_CODES: {
2
- OK: number;
3
- BAD_REQUEST: number;
4
- UN_AUTHORISED: number;
5
- NOT_FOUND: number;
6
- INTERNAL_ERROR: number;
7
- };
@@ -1,7 +0,0 @@
1
- export const STATUS_CODES = {
2
- OK: 200,
3
- BAD_REQUEST: 400,
4
- UN_AUTHORISED: 403,
5
- NOT_FOUND: 404,
6
- INTERNAL_ERROR: 500,
7
- };
@@ -1 +0,0 @@
1
- export declare const ValidateError: (input: any) => Promise<Record<string, any> | false>;
@@ -1,14 +0,0 @@
1
- import { validate } from "class-validator";
2
- export const ValidateError = async (input) => {
3
- const error = await validate(input, {
4
- ValidationError: { target: true, property: true },
5
- });
6
- if (error.length) {
7
- return error.map((err) => ({
8
- field: err.property,
9
- message: (err.constraints && Object.values(err.constraints)[0]) ||
10
- "please provide input for this field",
11
- }));
12
- }
13
- return false;
14
- };
@@ -1,9 +0,0 @@
1
- import type { Static, TSchema } from "@sinclair/typebox";
2
- export declare function sanitizeStrings<T>(input: T): T;
3
- export declare function ValidateRequest<T extends TSchema>(data: unknown, schema: T): {
4
- valid: true;
5
- data: Static<T>;
6
- } | {
7
- valid: false;
8
- error: string;
9
- };
@@ -1,43 +0,0 @@
1
- import Ajv from "ajv";
2
- import ajvErrors from "ajv-errors";
3
- import addFormats from "ajv-formats";
4
- const ajv = new Ajv({ allErrors: true, strict: false, coerceTypes: true });
5
- ajvErrors(ajv);
6
- addFormats(ajv);
7
- export function sanitizeStrings(input) {
8
- if (input === null || input === undefined) {
9
- return input;
10
- }
11
- if (typeof input === "string") {
12
- return input.trim();
13
- }
14
- // Handle arrays recursively
15
- if (Array.isArray(input)) {
16
- return input.map((item) => sanitizeStrings(item));
17
- }
18
- // Handle objects recursively
19
- if (typeof input === "object") {
20
- const result = {};
21
- for (const key in input) {
22
- if (Object.prototype.hasOwnProperty.call(input, key)) {
23
- result[key] = sanitizeStrings(input[key]);
24
- }
25
- }
26
- return result;
27
- }
28
- // Return non-string primitive values as-is
29
- return input;
30
- }
31
- // Generic validator function using TypeBox schemas
32
- export function ValidateRequest(data, schema) {
33
- const sanitizedData = sanitizeStrings(data);
34
- const validate = ajv.compile(schema);
35
- const isValid = validate(sanitizedData);
36
- if (isValid)
37
- return { valid: true, data: sanitizedData };
38
- const errorMessages = validate.errors?.map((e) => e.message).filter(Boolean);
39
- return {
40
- valid: false,
41
- error: errorMessages?.[0] || "Validation failed",
42
- };
43
- }