fiberx-backend-toolkit 0.0.57 → 0.0.58

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.
@@ -0,0 +1,4 @@
1
+ declare const GENERATE_NOTIFICATION_CODE_TYPE: (interface_name: string, static_obj: any, db_obj: any, code: string) => string;
2
+ declare const GENERATE_UTIL_METHOD_CODE: (interface_name: string, method_name: string, code: string) => string;
3
+ declare const GENERATE_UTIL_CLASS_CODE: (types_file_name: string, generated_content: string) => string;
4
+ export { GENERATE_NOTIFICATION_CODE_TYPE, GENERATE_UTIL_METHOD_CODE, GENERATE_UTIL_CLASS_CODE };
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GENERATE_UTIL_CLASS_CODE = exports.GENERATE_UTIL_METHOD_CODE = exports.GENERATE_NOTIFICATION_CODE_TYPE = void 0;
4
+ const objectToType = (obj, indent) => {
5
+ const spacing = " ".repeat(indent);
6
+ return Object.entries(obj)
7
+ .map(([key, value]) => {
8
+ if (value === "string") {
9
+ return `${spacing}${key}: string;`;
10
+ }
11
+ return `${spacing}${key}: {\n${objectToType(value, indent + 4)}\n${spacing}};`;
12
+ })
13
+ .join("\n");
14
+ };
15
+ const GENERATE_NOTIFICATION_CODE_TYPE = (interface_name, static_obj, db_obj, code) => {
16
+ return `
17
+ /**
18
+ * @generated-notification-code: ${code}
19
+ */
20
+ export interface ${interface_name} {
21
+ static_data: {
22
+ ${objectToType(static_obj, 8)}
23
+ };
24
+ db_data: {
25
+ ${objectToType(db_obj, 8)}
26
+ };
27
+ }
28
+ `;
29
+ };
30
+ exports.GENERATE_NOTIFICATION_CODE_TYPE = GENERATE_NOTIFICATION_CODE_TYPE;
31
+ const GENERATE_UTIL_METHOD_CODE = (interface_name, method_name, code) => {
32
+ return `
33
+ /**
34
+ * @generated-notification-code: ${code}
35
+ */
36
+ public static async ${method_name}(params: {
37
+ recipient_email: string;
38
+ created_by?: number | null;
39
+ payload: ${interface_name};
40
+ }) {
41
+ return EmailQueueService.enqueueEmail<${interface_name}>({
42
+ notification_code: "${code}",
43
+ recipient_email: params.recipient_email,
44
+ created_by: params.created_by ?? null,
45
+ payload: params.payload,
46
+ });
47
+ }
48
+ `;
49
+ };
50
+ exports.GENERATE_UTIL_METHOD_CODE = GENERATE_UTIL_METHOD_CODE;
51
+ const GENERATE_UTIL_CLASS_CODE = (types_file_name, generated_content) => {
52
+ return `
53
+ import { EmailQueueService } from "@/services/email_queue_service";
54
+ import * as Types from "@/${types_file_name}";
55
+
56
+ class GeneratedEmailEnqueueUtil {
57
+ ${generated_content}
58
+ }
59
+
60
+ export default GeneratedEmailEnqueueUtil;
61
+ `;
62
+ };
63
+ exports.GENERATE_UTIL_CLASS_CODE = GENERATE_UTIL_CLASS_CODE;
@@ -6,6 +6,7 @@ export declare const SCHEMA_SNAPSHOTS_DIR: string;
6
6
  export declare const MODELS_DIR: string;
7
7
  export declare const MIGRATIONS_DIR: string;
8
8
  export declare const SEEDERS_DIR: string;
9
+ export declare const EMAIL_ENQUEUE_DIR: string;
9
10
  export declare const SEQUELIZE_META_TABLE_NAME = "sequelize_database_tables_meta";
10
11
  export declare const SEQUELIZE_SEEDER_META_TABLE_NAME = "sequelize_database_table_seeder_meta";
11
12
  export declare const REQUEST_ID_COOKIE_MAX_AGE: number;
@@ -30,3 +31,5 @@ export declare const DEFAULT_TOTP_DIGITS = 6;
30
31
  export declare const DEFAULT_TOTP_WINDOW = 1;
31
32
  export declare const DEFAULT_RBAC_CACHE_KEY = "RBAC_SNAPSHOT";
32
33
  export declare const DEFAULT_RBAC_CACHE_TTL: number;
34
+ export declare const EMAIL_ENQUEUE_TYPES_FILE_NAME = "types/email_enqueue_type.generated.ts";
35
+ export declare const EMAIL_ENQUEUE_UTIL_FILE_NAME = "utils/email_enqueue_util.generated.ts";
@@ -3,9 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.DEFAULT_RBAC_CACHE_TTL = exports.DEFAULT_RBAC_CACHE_KEY = exports.DEFAULT_TOTP_WINDOW = exports.DEFAULT_TOTP_DIGITS = exports.DEFAULT_TOTP_STEP = exports.ALPHABET_CORPUS = exports.CHARACTER_CORPUS = exports.REQUEST_RATE_LIMITTER_OPTIONS = exports.CORS_MAX_AGE_IN_MICRO_SECONDS = exports.CORS_MAX_AGE_IN_SECONDS = exports.CORS_ALLOWED_HEADERS = exports.CORS_ALLOWED_METHODS = exports.DEVICE_ID_HEADERS_NAME = exports.DEVICE_ID_COOKIE_NAME = exports.DEVICE_ID_COOKIE_MAX_AGE = exports.REQUEST_ID_HEADERS_NAME = exports.REQUEST_ID_COOKIE_NAME = exports.REQUEST_ID_COOKIE_MAX_AGE = exports.SEQUELIZE_SEEDER_META_TABLE_NAME = exports.SEQUELIZE_META_TABLE_NAME = exports.SEEDERS_DIR = exports.MIGRATIONS_DIR = exports.MODELS_DIR = exports.SCHEMA_SNAPSHOTS_DIR = exports.SCHEMAS_DIR = exports.ENV_VAR_DIR = exports.LOG_DIR = exports.BASE_DIR = void 0;
6
+ exports.EMAIL_ENQUEUE_UTIL_FILE_NAME = exports.EMAIL_ENQUEUE_TYPES_FILE_NAME = exports.DEFAULT_RBAC_CACHE_TTL = exports.DEFAULT_RBAC_CACHE_KEY = exports.DEFAULT_TOTP_WINDOW = exports.DEFAULT_TOTP_DIGITS = exports.DEFAULT_TOTP_STEP = exports.ALPHABET_CORPUS = exports.CHARACTER_CORPUS = exports.REQUEST_RATE_LIMITTER_OPTIONS = exports.CORS_MAX_AGE_IN_MICRO_SECONDS = exports.CORS_MAX_AGE_IN_SECONDS = exports.CORS_ALLOWED_HEADERS = exports.CORS_ALLOWED_METHODS = exports.DEVICE_ID_HEADERS_NAME = exports.DEVICE_ID_COOKIE_NAME = exports.DEVICE_ID_COOKIE_MAX_AGE = exports.REQUEST_ID_HEADERS_NAME = exports.REQUEST_ID_COOKIE_NAME = exports.REQUEST_ID_COOKIE_MAX_AGE = exports.SEQUELIZE_SEEDER_META_TABLE_NAME = exports.SEQUELIZE_META_TABLE_NAME = exports.EMAIL_ENQUEUE_DIR = exports.SEEDERS_DIR = exports.MIGRATIONS_DIR = exports.MODELS_DIR = exports.SCHEMA_SNAPSHOTS_DIR = exports.SCHEMAS_DIR = exports.ENV_VAR_DIR = exports.LOG_DIR = exports.BASE_DIR = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
- // Database constants
9
8
  exports.BASE_DIR = process.cwd();
10
9
  exports.LOG_DIR = path_1.default.join(exports.BASE_DIR, "logs");
11
10
  exports.ENV_VAR_DIR = path_1.default.join(exports.BASE_DIR, "environment_varaiables");
@@ -14,6 +13,7 @@ exports.SCHEMA_SNAPSHOTS_DIR = path_1.default.join(exports.BASE_DIR, "src/databa
14
13
  exports.MODELS_DIR = path_1.default.join(exports.BASE_DIR, "src/database/models");
15
14
  exports.MIGRATIONS_DIR = path_1.default.join(exports.BASE_DIR, "src/database/migrations");
16
15
  exports.SEEDERS_DIR = path_1.default.join(exports.BASE_DIR, "src/database/seeders");
16
+ exports.EMAIL_ENQUEUE_DIR = path_1.default.join(exports.BASE_DIR, "src/");
17
17
  exports.SEQUELIZE_META_TABLE_NAME = "sequelize_database_tables_meta";
18
18
  exports.SEQUELIZE_SEEDER_META_TABLE_NAME = "sequelize_database_table_seeder_meta";
19
19
  exports.REQUEST_ID_COOKIE_MAX_AGE = (1000 * 60 * 60 * 24 * 30); // 7 days
@@ -40,3 +40,5 @@ exports.DEFAULT_TOTP_DIGITS = 6;
40
40
  exports.DEFAULT_TOTP_WINDOW = 1;
41
41
  exports.DEFAULT_RBAC_CACHE_KEY = "RBAC_SNAPSHOT";
42
42
  exports.DEFAULT_RBAC_CACHE_TTL = (1000 * 60 * 60 * 24); // 24 hours
43
+ exports.EMAIL_ENQUEUE_TYPES_FILE_NAME = "types/email_enqueue_type.generated.ts";
44
+ exports.EMAIL_ENQUEUE_UTIL_FILE_NAME = "utils/email_enqueue_util.generated.ts";
@@ -0,0 +1,21 @@
1
+ import { EmailEnqueueGeneratorOptions, EmailEnqueueDataProvider } from "../../types/mailer_type";
2
+ declare class EmailEnqueueGenerator<TTemplate, TNotification> {
3
+ private readonly provider;
4
+ private static instance;
5
+ readonly name: string;
6
+ private readonly logger;
7
+ private readonly output_dir;
8
+ private readonly types_file_path;
9
+ private readonly util_file_path;
10
+ private readonly batch_size;
11
+ private readonly options;
12
+ private constructor();
13
+ static initialize<TTemplate, TNotification>(provider: EmailEnqueueDataProvider<TTemplate, TNotification>, options: EmailEnqueueGeneratorOptions): EmailEnqueueGenerator<TTemplate, TNotification>;
14
+ static getInstance<TTemplate, TNotification>(): EmailEnqueueGenerator<TTemplate, TNotification>;
15
+ private extractExistingNotificationCodes;
16
+ private appendType;
17
+ private appendUtilMethod;
18
+ private generateForTemplate;
19
+ generate(): Promise<void>;
20
+ }
21
+ export default EmailEnqueueGenerator;
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_1 = __importDefault(require("fs"));
7
+ const path_1 = __importDefault(require("path"));
8
+ const constants_1 = require("../../config/constants");
9
+ const main_1 = require("../../utils/main");
10
+ const email_enqueue_code_template_1 = require("../../code_templates/email_enqueue_code_template");
11
+ class EmailEnqueueGenerator {
12
+ provider;
13
+ static instance = null;
14
+ name = "email_enqueue_generator";
15
+ logger = new main_1.LoggerUtil(this.name);
16
+ output_dir;
17
+ types_file_path;
18
+ util_file_path;
19
+ batch_size;
20
+ options;
21
+ constructor(provider, options) {
22
+ this.provider = provider;
23
+ this.options = options;
24
+ this.output_dir = options.output_dir ?? constants_1.EMAIL_ENQUEUE_DIR;
25
+ this.batch_size = options.batch_size ?? 200;
26
+ this.types_file_path = path_1.default.join(this.output_dir, options.types_file_name ?? constants_1.EMAIL_ENQUEUE_TYPES_FILE_NAME);
27
+ this.util_file_path = path_1.default.join(this.output_dir, options.util_file_name ?? constants_1.EMAIL_ENQUEUE_UTIL_FILE_NAME);
28
+ }
29
+ // ===============================
30
+ // SINGLETON
31
+ // ===============================
32
+ static initialize(provider, options) {
33
+ if (this.instance) {
34
+ throw new Error("EmailEnqueueGenerator already initialized.");
35
+ }
36
+ this.instance = new EmailEnqueueGenerator(provider, options);
37
+ return this.instance;
38
+ }
39
+ static getInstance() {
40
+ if (!this.instance) {
41
+ throw new Error("EmailEnqueueGeneratorUtil not initialized.");
42
+ }
43
+ return this.instance;
44
+ }
45
+ // Method to extrace exisitng rendered code from class methods
46
+ extractExistingNotificationCodes() {
47
+ try {
48
+ if (!fs_1.default.existsSync(this.types_file_path)) {
49
+ this.logger.info("📂 Types file not found. No existing codes detected.");
50
+ return new Set();
51
+ }
52
+ const content = fs_1.default.readFileSync(this.types_file_path, "utf8");
53
+ const regex = /@generated-notification-code:\s*(\w+)/g;
54
+ const codes = new Set();
55
+ let match;
56
+ while ((match = regex.exec(content)) !== null) {
57
+ codes.add(match[1]);
58
+ }
59
+ this.logger.info(`🔎 Found ${codes.size} existing generated notification codes.`);
60
+ return codes;
61
+ }
62
+ catch (error) {
63
+ this.logger.error("❌ Failed to extract existing notification codes.", { error });
64
+ throw error;
65
+ }
66
+ }
67
+ // Method to append generated to type to output file type
68
+ appendType(interface_name, static_obj, db_obj, code) {
69
+ try {
70
+ const content = (0, email_enqueue_code_template_1.GENERATE_NOTIFICATION_CODE_TYPE)(interface_name, static_obj, db_obj, code);
71
+ fs_1.default.appendFileSync(this.types_file_path, content);
72
+ this.logger.info(`🧩 Type generated → ${interface_name} (${code})`);
73
+ return true;
74
+ }
75
+ catch (error) {
76
+ this.logger.error(`❌ Failed generating type for ${code}`, { error });
77
+ return false;
78
+ }
79
+ }
80
+ appendUtilMethod(interface_name, method_name, code) {
81
+ try {
82
+ const content = (0, email_enqueue_code_template_1.GENERATE_UTIL_METHOD_CODE)(interface_name, method_name, code);
83
+ // First time creation
84
+ if (!fs_1.default.existsSync(this.util_file_path)) {
85
+ const type_file_name = this.options.types_file_name ?? constants_1.EMAIL_ENQUEUE_TYPES_FILE_NAME;
86
+ const full_class_content = (0, email_enqueue_code_template_1.GENERATE_UTIL_CLASS_CODE)(type_file_name, content);
87
+ fs_1.default.writeFileSync(this.util_file_path, full_class_content);
88
+ this.logger.info(`📦 Created new util file and added method → ${method_name}`);
89
+ return true;
90
+ }
91
+ // Append inside existing class
92
+ const existing = fs_1.default.readFileSync(this.util_file_path, "utf8");
93
+ if (!existing.trim().endsWith("}")) {
94
+ this.logger.error("❌ Util file malformed — missing closing class bracket.");
95
+ return false;
96
+ }
97
+ const updated = existing.replace(/}\s*$/, `${content}\n}`);
98
+ fs_1.default.writeFileSync(this.util_file_path, updated);
99
+ this.logger.info(`⚙️ Appended method → ${method_name}`);
100
+ return true;
101
+ }
102
+ catch (error) {
103
+ this.logger.error(`❌ Failed generating util method for ${code}`, { error });
104
+ return false;
105
+ }
106
+ }
107
+ // Method to generate template code
108
+ async generateForTemplate(template) {
109
+ try {
110
+ const notification = this.provider.getNotificationFromTemplate(template);
111
+ const code = this.provider.getNotificationCode(notification);
112
+ const interface_name = main_1.InputTransformerUtil.toPascalCase(code) + "EmailPayload";
113
+ const method_name = "send" + main_1.InputTransformerUtil.toPascalCase(code);
114
+ const placeholders = this.provider.getRequiredPlaceholders(template) ?? {};
115
+ const static_obj = main_1.InputTransformerUtil.buildNestedObject(placeholders.static_data ?? []);
116
+ const db_obj = main_1.InputTransformerUtil.buildNestedObject(placeholders.db_data ?? []);
117
+ this.logger.info(`🚀 Generating email enqueue code for → ${code}`);
118
+ const type_success = this.appendType(interface_name, static_obj, db_obj, code);
119
+ const method_success = this.appendUtilMethod(interface_name, method_name, code);
120
+ if (type_success && method_success) {
121
+ this.logger.success?.(`✅ Successfully generated enqueue method for code → ${code}`);
122
+ }
123
+ else {
124
+ this.logger.alert?.(`⚠️ Partial generation of enqueue method for code → ${code}`);
125
+ }
126
+ }
127
+ catch (error) {
128
+ this.logger.error("❌ Failed generating template code.", { error });
129
+ throw error;
130
+ }
131
+ }
132
+ // ===============================
133
+ // PUBLIC GENERATE
134
+ // ===============================
135
+ async generate() {
136
+ try {
137
+ this.logger.info("📧 Starting email enqueue code generation...");
138
+ main_1.InputValidatorUtil.dirExists(this.output_dir, true);
139
+ const existing_codes = this.extractExistingNotificationCodes();
140
+ const templates = await this.provider.fetchActiveTemplates(Array.from(existing_codes), this.batch_size);
141
+ if (!templates.length) {
142
+ this.logger.info("✨ No new templates to generate.");
143
+ return;
144
+ }
145
+ this.logger.info(`📝 ${templates.length} new template(s) detected.`);
146
+ for (const template of templates) {
147
+ await this.generateForTemplate(template);
148
+ }
149
+ this.logger.info(`🎉 Generation complete. ${templates.length} notification(s) processed.`);
150
+ }
151
+ catch (error) {
152
+ this.logger.error("🔥 Email enqueue generation failed.", { error });
153
+ throw error;
154
+ }
155
+ }
156
+ }
157
+ exports.default = EmailEnqueueGenerator;
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,16 @@
1
+ export interface EmailEnqueueGeneratorOptions {
2
+ output_dir: string;
3
+ types_file_name?: string;
4
+ util_file_name?: string;
5
+ batch_size?: number;
6
+ }
7
+ export interface EmailTemplatePlaceholders {
8
+ static_data?: string[];
9
+ db_data?: string[];
10
+ }
11
+ export interface EmailEnqueueDataProvider<TTemplate, TNotification> {
12
+ fetchActiveTemplates(exclude_codes: string[], limit: number): Promise<TTemplate[]>;
13
+ getNotificationFromTemplate(template: TTemplate): TNotification;
14
+ getNotificationCode(notification: TNotification): string;
15
+ getRequiredPlaceholders(template: TTemplate): EmailTemplatePlaceholders | null;
16
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -111,5 +111,6 @@ declare class InputTransformerUtil {
111
111
  * Normalize social links object keys
112
112
  */
113
113
  static formatLinksObject(links: Record<string, string>): Record<string, string>;
114
+ static buildNestedObject(paths: string[]): any;
114
115
  }
115
116
  export default InputTransformerUtil;
@@ -310,5 +310,22 @@ class InputTransformerUtil {
310
310
  }
311
311
  return formatted_links;
312
312
  }
313
+ // Method to build nested object from dotted pats
314
+ static buildNestedObject(paths) {
315
+ const root = {};
316
+ for (const p of paths) {
317
+ const parts = p.split(".");
318
+ let current = root;
319
+ for (let i = 0; i < parts.length; i++) {
320
+ const key = parts[i];
321
+ if (!current[key]) {
322
+ current[key] =
323
+ i === parts.length - 1 ? "string" : {};
324
+ }
325
+ current = current[key];
326
+ }
327
+ }
328
+ return root;
329
+ }
313
330
  }
314
331
  exports.default = InputTransformerUtil;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fiberx-backend-toolkit",
3
- "version": "0.0.57",
3
+ "version": "0.0.58",
4
4
  "description": "A TypeScript backend toolkit providing shared domain logic, infrastructure helpers, and utilities for FiberX server-side applications and services.",
5
5
  "type": "commonjs",
6
6
  "main": "./dist/index.js",