saas-backend-kit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/PUBLISHING.md +133 -0
  3. package/README.md +459 -0
  4. package/copy-dts.js +255 -0
  5. package/dist/auth/index.d.ts +58 -0
  6. package/dist/auth/index.js +584 -0
  7. package/dist/auth/index.js.map +1 -0
  8. package/dist/auth/index.mjs +569 -0
  9. package/dist/auth/index.mjs.map +1 -0
  10. package/dist/config/index.d.ts +22 -0
  11. package/dist/config/index.js +106 -0
  12. package/dist/config/index.js.map +1 -0
  13. package/dist/config/index.mjs +100 -0
  14. package/dist/config/index.mjs.map +1 -0
  15. package/dist/index.d.ts +25 -0
  16. package/dist/index.js +1303 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/index.mjs +1281 -0
  19. package/dist/index.mjs.map +1 -0
  20. package/dist/logger/index.d.ts +18 -0
  21. package/dist/logger/index.js +188 -0
  22. package/dist/logger/index.js.map +1 -0
  23. package/dist/logger/index.mjs +178 -0
  24. package/dist/logger/index.mjs.map +1 -0
  25. package/dist/notifications/index.d.ts +35 -0
  26. package/dist/notifications/index.js +339 -0
  27. package/dist/notifications/index.js.map +1 -0
  28. package/dist/notifications/index.mjs +328 -0
  29. package/dist/notifications/index.mjs.map +1 -0
  30. package/dist/queue/index.d.ts +33 -0
  31. package/dist/queue/index.js +306 -0
  32. package/dist/queue/index.js.map +1 -0
  33. package/dist/queue/index.mjs +293 -0
  34. package/dist/queue/index.mjs.map +1 -0
  35. package/dist/rate-limit/index.d.ts +11 -0
  36. package/dist/rate-limit/index.js +290 -0
  37. package/dist/rate-limit/index.js.map +1 -0
  38. package/dist/rate-limit/index.mjs +286 -0
  39. package/dist/rate-limit/index.mjs.map +1 -0
  40. package/dist/response/index.d.ts +29 -0
  41. package/dist/response/index.js +120 -0
  42. package/dist/response/index.js.map +1 -0
  43. package/dist/response/index.mjs +114 -0
  44. package/dist/response/index.mjs.map +1 -0
  45. package/examples/express/.env.example +41 -0
  46. package/examples/express/app.ts +203 -0
  47. package/package.json +109 -0
  48. package/src/auth/express.ts +250 -0
  49. package/src/auth/fastify.ts +65 -0
  50. package/src/auth/index.ts +6 -0
  51. package/src/auth/jwt.ts +47 -0
  52. package/src/auth/oauth.ts +117 -0
  53. package/src/auth/rbac.ts +82 -0
  54. package/src/auth/types.ts +69 -0
  55. package/src/config/index.ts +120 -0
  56. package/src/index.ts +16 -0
  57. package/src/logger/index.ts +110 -0
  58. package/src/notifications/index.ts +262 -0
  59. package/src/plugin.ts +192 -0
  60. package/src/queue/index.ts +208 -0
  61. package/src/rate-limit/express.ts +144 -0
  62. package/src/rate-limit/fastify.ts +47 -0
  63. package/src/rate-limit/index.ts +2 -0
  64. package/src/response/index.ts +197 -0
  65. package/src/utils/index.ts +180 -0
  66. package/tsconfig.json +30 -0
  67. package/tsup.config.ts +24 -0
@@ -0,0 +1,306 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var bullmq = require('bullmq');
6
+ var zod = require('zod');
7
+ var pino = require('pino');
8
+
9
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
+
11
+ var pino__default = /*#__PURE__*/_interopDefault(pino);
12
+
13
+ // src/queue/index.ts
14
+ var envSchema = zod.z.object({
15
+ NODE_ENV: zod.z.enum(["development", "production", "test"]).default("development"),
16
+ PORT: zod.z.string().default("3000"),
17
+ DATABASE_URL: zod.z.string().optional(),
18
+ REDIS_URL: zod.z.string().default("redis://localhost:6379"),
19
+ JWT_SECRET: zod.z.string().min(32).optional(),
20
+ JWT_EXPIRES_IN: zod.z.string().default("7d"),
21
+ JWT_REFRESH_SECRET: zod.z.string().min(32).optional(),
22
+ JWT_REFRESH_EXPIRES_IN: zod.z.string().default("30d"),
23
+ GOOGLE_CLIENT_ID: zod.z.string().optional(),
24
+ GOOGLE_CLIENT_SECRET: zod.z.string().optional(),
25
+ GOOGLE_REDIRECT_URI: zod.z.string().optional(),
26
+ SMTP_HOST: zod.z.string().optional(),
27
+ SMTP_PORT: zod.z.string().default("587"),
28
+ SMTP_USER: zod.z.string().optional(),
29
+ SMTP_PASS: zod.z.string().optional(),
30
+ SMTP_FROM: zod.z.string().optional(),
31
+ TWILIO_ACCOUNT_SID: zod.z.string().optional(),
32
+ TWILIO_AUTH_TOKEN: zod.z.string().optional(),
33
+ TWILIO_PHONE_NUMBER: zod.z.string().optional(),
34
+ SLACK_WEBHOOK_URL: zod.z.string().optional(),
35
+ RATE_LIMIT_WINDOW: zod.z.string().default("1m"),
36
+ RATE_LIMIT_LIMIT: zod.z.string().default("100"),
37
+ LOG_LEVEL: zod.z.enum(["fatal", "error", "warn", "info", "debug", "trace"]).default("info")
38
+ });
39
+ var ConfigManager = class {
40
+ config = null;
41
+ schema;
42
+ validate;
43
+ constructor(options = {}) {
44
+ this.schema = options.schema || envSchema;
45
+ this.validate = options.validate ?? true;
46
+ }
47
+ load() {
48
+ if (this.config) return this.config;
49
+ const env = {};
50
+ for (const key of Object.keys(this.schema.shape)) {
51
+ env[key] = process.env[key];
52
+ }
53
+ if (this.validate) {
54
+ const result = this.schema.safeParse(env);
55
+ if (!result.success) {
56
+ const errors = result.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
57
+ throw new Error(`Config validation failed: ${errors}`);
58
+ }
59
+ this.config = result.data;
60
+ } else {
61
+ this.config = env;
62
+ }
63
+ return this.config;
64
+ }
65
+ get(key) {
66
+ if (!this.config) this.load();
67
+ return this.config[key];
68
+ }
69
+ int(key) {
70
+ const value = this.get(key);
71
+ if (typeof value === "string") return parseInt(value, 10);
72
+ return Number(value);
73
+ }
74
+ bool(key) {
75
+ const value = this.get(key);
76
+ if (typeof value === "boolean") return value;
77
+ if (typeof value === "string") return value.toLowerCase() === "true";
78
+ return Boolean(value);
79
+ }
80
+ isProduction() {
81
+ return this.get("NODE_ENV") === "production";
82
+ }
83
+ isDevelopment() {
84
+ return this.get("NODE_ENV") === "development";
85
+ }
86
+ isTest() {
87
+ return this.get("NODE_ENV") === "test";
88
+ }
89
+ getAll() {
90
+ if (!this.config) this.load();
91
+ return this.config;
92
+ }
93
+ };
94
+ var globalConfig = new ConfigManager();
95
+ var config = {
96
+ load: () => globalConfig.load(),
97
+ get: (key) => globalConfig.get(key),
98
+ int: (key) => globalConfig.int(key),
99
+ bool: (key) => globalConfig.bool(key),
100
+ isProduction: () => globalConfig.isProduction(),
101
+ isDevelopment: () => globalConfig.isDevelopment(),
102
+ isTest: () => globalConfig.isTest(),
103
+ getAll: () => globalConfig.getAll(),
104
+ create: (options) => new ConfigManager(options)
105
+ };
106
+ var LoggerManager = class {
107
+ loggers = /* @__PURE__ */ new Map();
108
+ defaultLogger;
109
+ constructor() {
110
+ const level = config.get("LOG_LEVEL") || "info";
111
+ this.defaultLogger = pino__default.default({
112
+ level,
113
+ name: "saas-backend-kit",
114
+ formatters: {
115
+ bindings: (bindings) => ({
116
+ ...bindings,
117
+ service: "saas-backend-kit"
118
+ })
119
+ }
120
+ });
121
+ }
122
+ createLogger(options = {}) {
123
+ const name = options.name || "default";
124
+ if (this.loggers.has(name)) {
125
+ return this.loggers.get(name);
126
+ }
127
+ const level = options.level || config.get("LOG_LEVEL") || "info";
128
+ const logger2 = pino__default.default({
129
+ level,
130
+ name: options.name,
131
+ ...options
132
+ });
133
+ this.loggers.set(name, logger2);
134
+ return logger2;
135
+ }
136
+ getLogger(name) {
137
+ if (name) {
138
+ return this.loggers.get(name) || this.defaultLogger;
139
+ }
140
+ return this.defaultLogger;
141
+ }
142
+ child(bindings, options) {
143
+ const name = options?.name || "child";
144
+ const parent = options?.name ? this.getLogger(name) : this.defaultLogger;
145
+ return parent.child(bindings);
146
+ }
147
+ };
148
+ var loggerManager = new LoggerManager();
149
+ var logger = {
150
+ info: (message, ...args) => loggerManager.getLogger().info(message, ...args),
151
+ warn: (message, ...args) => loggerManager.getLogger().warn(message, ...args),
152
+ error: (message, ...args) => loggerManager.getLogger().error(message, ...args),
153
+ debug: (message, ...args) => loggerManager.getLogger().debug(message, ...args),
154
+ trace: (message, ...args) => loggerManager.getLogger().trace(message, ...args),
155
+ fatal: (message, ...args) => loggerManager.getLogger().fatal(message, ...args),
156
+ child: (bindings, options) => loggerManager.child(bindings, options),
157
+ create: (options) => loggerManager.createLogger(options),
158
+ get: (name) => loggerManager.getLogger(name)
159
+ };
160
+
161
+ // src/queue/index.ts
162
+ var QueueManager = class {
163
+ queues = /* @__PURE__ */ new Map();
164
+ workers = /* @__PURE__ */ new Map();
165
+ redisOptions;
166
+ constructor() {
167
+ const redisUrl = config.get("REDIS_URL");
168
+ if (redisUrl) {
169
+ this.redisOptions = { url: redisUrl };
170
+ } else {
171
+ this.redisOptions = {
172
+ host: "localhost",
173
+ port: 6379
174
+ };
175
+ }
176
+ }
177
+ setRedisOptions(options) {
178
+ this.redisOptions = options;
179
+ }
180
+ createQueue(name, options) {
181
+ if (this.queues.has(name)) {
182
+ return this.queues.get(name);
183
+ }
184
+ const queue2 = new bullmq.Queue(name, {
185
+ connection: this.redisOptions,
186
+ defaultJobOptions: options?.defaultJobOptions || {
187
+ removeOnComplete: 100,
188
+ removeOnFail: 100
189
+ }
190
+ });
191
+ this.queues.set(name, queue2);
192
+ logger.info(`Queue "${name}" created`);
193
+ return queue2;
194
+ }
195
+ getQueue(name) {
196
+ return this.queues.get(name);
197
+ }
198
+ async addJob(queueName, jobName, data, options) {
199
+ const queue2 = this.getQueue(queueName) || this.createQueue(queueName);
200
+ const job = await queue2.add(jobName, data, options);
201
+ logger.debug(`Job "${jobName}" added to queue "${queueName}"`, { jobId: job.id });
202
+ return job;
203
+ }
204
+ async addBulkJobs(queueName, jobs) {
205
+ const queue2 = this.getQueue(queueName) || this.createQueue(queueName);
206
+ const bulkJobs = jobs.map((job) => ({
207
+ name: job.name,
208
+ data: job.data,
209
+ ...job.options
210
+ }));
211
+ const result = await queue2.addBulk(bulkJobs);
212
+ logger.debug(`${jobs.length} jobs added to queue "${queueName}"`);
213
+ return result;
214
+ }
215
+ processJob(queueName, processor, options) {
216
+ this.getQueue(queueName) || this.createQueue(queueName);
217
+ const worker = new bullmq.Worker(queueName, async (job) => {
218
+ logger.debug(`Processing job "${job.name}"`, { jobId: job.id, queue: queueName });
219
+ return await processor(job);
220
+ }, {
221
+ connection: this.redisOptions,
222
+ concurrency: options?.concurrency || 1,
223
+ ...options
224
+ });
225
+ worker.on("completed", (job) => {
226
+ logger.debug(`Job completed`, { jobId: job.id, queue: queueName });
227
+ });
228
+ worker.on("failed", (job, err) => {
229
+ logger.error(`Job failed`, { jobId: job?.id, queue: queueName, error: err.message });
230
+ });
231
+ worker.on("error", (err) => {
232
+ logger.error(`Worker error`, { queue: queueName, error: err.message });
233
+ });
234
+ this.workers.set(queueName, worker);
235
+ logger.info(`Worker started for queue "${queueName}"`);
236
+ return worker;
237
+ }
238
+ async getJobCounts(queueName) {
239
+ const queue2 = this.getQueue(queueName);
240
+ if (!queue2) {
241
+ return { waiting: 0, active: 0, completed: 0, failed: 0, delayed: 0 };
242
+ }
243
+ return await queue2.getJobCounts();
244
+ }
245
+ async getJobs(queueName, start = 0, end = 10) {
246
+ const queue2 = this.getQueue(queueName);
247
+ if (!queue2) return [];
248
+ return await queue2.getJobs(["waiting", "active", "completed", "failed"], start, end);
249
+ }
250
+ async closeQueue(name) {
251
+ const queue2 = this.queues.get(name);
252
+ if (queue2) {
253
+ await queue2.close();
254
+ this.queues.delete(name);
255
+ logger.info(`Queue "${name}" closed`);
256
+ }
257
+ }
258
+ async closeWorker(name) {
259
+ const worker = this.workers.get(name);
260
+ if (worker) {
261
+ await worker.close();
262
+ this.workers.delete(name);
263
+ logger.info(`Worker for queue "${name}" closed`);
264
+ }
265
+ }
266
+ async closeAll() {
267
+ await Promise.all([
268
+ ...Array.from(this.queues.values()).map((q) => q.close()),
269
+ ...Array.from(this.workers.values()).map((w) => w.close())
270
+ ]);
271
+ this.queues.clear();
272
+ this.workers.clear();
273
+ logger.info("All queues and workers closed");
274
+ }
275
+ };
276
+ var queueManager = new QueueManager();
277
+ var createQueue = (name, options) => {
278
+ return queueManager.createQueue(name, options);
279
+ };
280
+ var addJob = (queueName, jobName, data, options) => {
281
+ return queueManager.addJob(queueName, jobName, data, options);
282
+ };
283
+ var processJob = (queueName, processor, options) => {
284
+ return queueManager.processJob(queueName, processor, options);
285
+ };
286
+ var queue = {
287
+ create: createQueue,
288
+ add: addJob,
289
+ process: processJob,
290
+ get: (name) => queueManager.getQueue(name),
291
+ getJobCounts: (name) => queueManager.getJobCounts(name),
292
+ getJobs: (name, start, end) => queueManager.getJobs(name, start, end),
293
+ close: (name) => queueManager.closeQueue(name),
294
+ closeAll: () => queueManager.closeAll(),
295
+ setRedisOptions: (options) => queueManager.setRedisOptions(options)
296
+ };
297
+ var queue_default = queue;
298
+
299
+ exports.QueueManager = QueueManager;
300
+ exports.addJob = addJob;
301
+ exports.createQueue = createQueue;
302
+ exports.default = queue_default;
303
+ exports.processJob = processJob;
304
+ exports.queue = queue;
305
+ //# sourceMappingURL=index.js.map
306
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/config/index.ts","../../src/logger/index.ts","../../src/queue/index.ts"],"names":["z","pino","logger","queue","Queue","Worker"],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\n\nexport const envSchema = z.object({\n NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n PORT: z.string().default('3000'),\n DATABASE_URL: z.string().optional(),\n REDIS_URL: z.string().default('redis://localhost:6379'),\n JWT_SECRET: z.string().min(32).optional(),\n JWT_EXPIRES_IN: z.string().default('7d'),\n JWT_REFRESH_SECRET: z.string().min(32).optional(),\n JWT_REFRESH_EXPIRES_IN: z.string().default('30d'),\n GOOGLE_CLIENT_ID: z.string().optional(),\n GOOGLE_CLIENT_SECRET: z.string().optional(),\n GOOGLE_REDIRECT_URI: z.string().optional(),\n SMTP_HOST: z.string().optional(),\n SMTP_PORT: z.string().default('587'),\n SMTP_USER: z.string().optional(),\n SMTP_PASS: z.string().optional(),\n SMTP_FROM: z.string().optional(),\n TWILIO_ACCOUNT_SID: z.string().optional(),\n TWILIO_AUTH_TOKEN: z.string().optional(),\n TWILIO_PHONE_NUMBER: z.string().optional(),\n SLACK_WEBHOOK_URL: z.string().optional(),\n RATE_LIMIT_WINDOW: z.string().default('1m'),\n RATE_LIMIT_LIMIT: z.string().default('100'),\n LOG_LEVEL: z.enum(['fatal', 'error', 'warn', 'info', 'debug', 'trace']).default('info'),\n});\n\nexport type EnvConfig = z.infer<typeof envSchema>;\n\nexport interface ConfigOptions {\n schema?: z.ZodSchema;\n envPath?: string;\n validate?: boolean;\n}\n\nclass ConfigManager {\n private config: EnvConfig | null = null;\n private schema: z.ZodSchema;\n private validate: boolean;\n\n constructor(options: ConfigOptions = {}) {\n this.schema = options.schema || envSchema;\n this.validate = options.validate ?? true;\n }\n\n load(): EnvConfig {\n if (this.config) return this.config;\n\n const env: Record<string, string | undefined> = {};\n \n for (const key of Object.keys(this.schema.shape)) {\n env[key] = process.env[key];\n }\n\n if (this.validate) {\n const result = this.schema.safeParse(env);\n if (!result.success) {\n const errors = result.error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ');\n throw new Error(`Config validation failed: ${errors}`);\n }\n this.config = result.data;\n } else {\n this.config = env as EnvConfig;\n }\n\n return this.config;\n }\n\n get<K extends keyof EnvConfig>(key: K): EnvConfig[K] {\n if (!this.config) this.load();\n return this.config![key];\n }\n\n int(key: keyof EnvConfig): number {\n const value = this.get(key);\n if (typeof value === 'string') return parseInt(value, 10);\n return Number(value);\n }\n\n bool(key: keyof EnvConfig): boolean {\n const value = this.get(key);\n if (typeof value === 'boolean') return value;\n if (typeof value === 'string') return value.toLowerCase() === 'true';\n return Boolean(value);\n }\n\n isProduction(): boolean {\n return this.get('NODE_ENV') === 'production';\n }\n\n isDevelopment(): boolean {\n return this.get('NODE_ENV') === 'development';\n }\n\n isTest(): boolean {\n return this.get('NODE_ENV') === 'test';\n }\n\n getAll(): EnvConfig {\n if (!this.config) this.load();\n return this.config!;\n }\n}\n\nconst globalConfig = new ConfigManager();\n\nexport const config = {\n load: () => globalConfig.load(),\n get: <K extends keyof EnvConfig>(key: K) => globalConfig.get(key),\n int: (key: keyof EnvConfig) => globalConfig.int(key),\n bool: (key: keyof EnvConfig) => globalConfig.bool(key),\n isProduction: () => globalConfig.isProduction(),\n isDevelopment: () => globalConfig.isDevelopment(),\n isTest: () => globalConfig.isTest(),\n getAll: () => globalConfig.getAll(),\n create: (options?: ConfigOptions) => new ConfigManager(options),\n};\n\nexport default config;\n","import pino, { Logger, LoggerOptions, Bindings } from 'pino';\nimport { config } from '../config';\n\nexport type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';\n\nexport interface LoggerConfig extends Partial<LoggerOptions> {\n level?: LogLevel;\n name?: string;\n prettyPrint?: boolean;\n}\n\nexport interface RequestLoggerOptions {\n logLevel?: LogLevel;\n autoLogging?: boolean;\n}\n\nclass LoggerManager {\n private loggers: Map<string, Logger> = new Map();\n private defaultLogger: Logger;\n\n constructor() {\n const level = (config.get('LOG_LEVEL') as LogLevel) || 'info';\n this.defaultLogger = pino({\n level,\n name: 'saas-backend-kit',\n formatters: {\n bindings: (bindings: Bindings) => ({\n ...bindings,\n service: 'saas-backend-kit',\n }),\n },\n });\n }\n\n createLogger(options: LoggerConfig = {}): Logger {\n const name = options.name || 'default';\n \n if (this.loggers.has(name)) {\n return this.loggers.get(name)!;\n }\n\n const level = options.level || (config.get('LOG_LEVEL') as LogLevel) || 'info';\n \n const logger = pino({\n level,\n name: options.name,\n ...options,\n });\n\n this.loggers.set(name, logger);\n return logger;\n }\n\n getLogger(name?: string): Logger {\n if (name) {\n return this.loggers.get(name) || this.defaultLogger;\n }\n return this.defaultLogger;\n }\n\n child(bindings: Bindings, options?: { name?: string }): Logger {\n const name = options?.name || 'child';\n const parent = options?.name ? this.getLogger(name) : this.defaultLogger;\n return parent.child(bindings);\n }\n}\n\nconst loggerManager = new LoggerManager();\n\nexport const logger = {\n info: (message: string, ...args: unknown[]) => loggerManager.getLogger().info(message, ...args),\n warn: (message: string, ...args: unknown[]) => loggerManager.getLogger().warn(message, ...args),\n error: (message: string, ...args: unknown[]) => loggerManager.getLogger().error(message, ...args),\n debug: (message: string, ...args: unknown[]) => loggerManager.getLogger().debug(message, ...args),\n trace: (message: string, ...args: unknown[]) => loggerManager.getLogger().trace(message, ...args),\n fatal: (message: string, ...args: unknown[]) => loggerManager.getLogger().fatal(message, ...args),\n child: (bindings: Bindings, options?: { name?: string }) => loggerManager.child(bindings, options),\n create: (options?: LoggerConfig) => loggerManager.createLogger(options),\n get: (name?: string) => loggerManager.getLogger(name),\n};\n\nexport function createRequestLogger(options: RequestLoggerOptions = {}) {\n const logLevel = options.logLevel || 'info';\n const logger = loggerManager.getLogger('http');\n\n return function requestLogger(\n req: { method: string; url: string; headers: Record<string, string | string[] | undefined> },\n res: { statusCode: number; statusMessage?: string },\n elapsed: number\n ) {\n const log = logger.child({\n method: req.method,\n url: req.url,\n status: res.statusCode,\n responseTime: elapsed,\n ip: req.headers['x-forwarded-for'] || req.headers['x-real-ip'] || 'unknown',\n userAgent: req.headers['user-agent'],\n });\n\n if (res.statusCode >= 500) {\n log.error(`Request completed`);\n } else if (res.statusCode >= 400) {\n log.warn(`Request completed`);\n } else {\n log.info(`Request completed`);\n }\n };\n}\n\nexport default logger;\n","import { Queue, Worker, Job, JobsOptions, WorkerOptions } from 'bullmq';\nimport { config } from '../config';\nimport { logger } from '../logger';\n\nexport interface QueueOptions {\n name: string;\n defaultJobOptions?: JobsOptions;\n}\n\nexport interface RedisOptions {\n host?: string;\n port?: number;\n password?: string;\n db?: number;\n url?: string;\n}\n\nexport interface JobData {\n [key: string]: unknown;\n}\n\nexport type JobProcessor = (job: Job<JobData>) => Promise<unknown>;\n\nclass QueueManager {\n private queues: Map<string, Queue> = new Map();\n private workers: Map<string, Worker> = new Map();\n private redisOptions: RedisOptions;\n\n constructor() {\n const redisUrl = config.get('REDIS_URL');\n if (redisUrl) {\n this.redisOptions = { url: redisUrl };\n } else {\n this.redisOptions = {\n host: 'localhost',\n port: 6379,\n };\n }\n }\n\n setRedisOptions(options: RedisOptions): void {\n this.redisOptions = options;\n }\n\n createQueue(name: string, options?: Partial<QueueOptions>): Queue {\n if (this.queues.has(name)) {\n return this.queues.get(name)!;\n }\n\n const queue = new Queue(name, {\n connection: this.redisOptions as any,\n defaultJobOptions: options?.defaultJobOptions || {\n removeOnComplete: 100,\n removeOnFail: 100,\n },\n });\n\n this.queues.set(name, queue);\n logger.info(`Queue \"${name}\" created`);\n\n return queue;\n }\n\n getQueue(name: string): Queue | undefined {\n return this.queues.get(name);\n }\n\n async addJob(\n queueName: string,\n jobName: string,\n data: JobData,\n options?: JobsOptions\n ): Promise<Job> {\n const queue = this.getQueue(queueName) || this.createQueue(queueName);\n const job = await queue.add(jobName, data, options);\n logger.debug(`Job \"${jobName}\" added to queue \"${queueName}\"`, { jobId: job.id });\n return job;\n }\n\n async addBulkJobs(\n queueName: string,\n jobs: Array<{ name: string; data: JobData; options?: JobsOptions }>\n ): Promise<Job[]> {\n const queue = this.getQueue(queueName) || this.createQueue(queueName);\n const bulkJobs = jobs.map(job => ({\n name: job.name,\n data: job.data,\n ...job.options,\n }));\n const result = await queue.addBulk(bulkJobs);\n logger.debug(`${jobs.length} jobs added to queue \"${queueName}\"`);\n return result;\n }\n\n processJob(\n queueName: string,\n processor: JobProcessor,\n options?: WorkerOptions\n ): Worker {\n const queue = this.getQueue(queueName) || this.createQueue(queueName);\n \n const worker = new Worker(queueName, async (job) => {\n logger.debug(`Processing job \"${job.name}\"`, { jobId: job.id, queue: queueName });\n return await processor(job);\n }, {\n connection: this.redisOptions as any,\n concurrency: options?.concurrency || 1,\n ...options,\n });\n\n worker.on('completed', (job) => {\n logger.debug(`Job completed`, { jobId: job.id, queue: queueName });\n });\n\n worker.on('failed', (job, err) => {\n logger.error(`Job failed`, { jobId: job?.id, queue: queueName, error: err.message });\n });\n\n worker.on('error', (err) => {\n logger.error(`Worker error`, { queue: queueName, error: err.message });\n });\n\n this.workers.set(queueName, worker);\n logger.info(`Worker started for queue \"${queueName}\"`);\n\n return worker;\n }\n\n async getJobCounts(queueName: string): Promise<Record<string, number>> {\n const queue = this.getQueue(queueName);\n if (!queue) {\n return { waiting: 0, active: 0, completed: 0, failed: 0, delayed: 0 };\n }\n return await queue.getJobCounts();\n }\n\n async getJobs(queueName: string, start: number = 0, end: number = 10): Promise<Job[]> {\n const queue = this.getQueue(queueName);\n if (!queue) return [];\n return await queue.getJobs(['waiting', 'active', 'completed', 'failed'], start, end);\n }\n\n async closeQueue(name: string): Promise<void> {\n const queue = this.queues.get(name);\n if (queue) {\n await queue.close();\n this.queues.delete(name);\n logger.info(`Queue \"${name}\" closed`);\n }\n }\n\n async closeWorker(name: string): Promise<void> {\n const worker = this.workers.get(name);\n if (worker) {\n await worker.close();\n this.workers.delete(name);\n logger.info(`Worker for queue \"${name}\" closed`);\n }\n }\n\n async closeAll(): Promise<void> {\n await Promise.all([\n ...Array.from(this.queues.values()).map(q => q.close()),\n ...Array.from(this.workers.values()).map(w => w.close()),\n ]);\n this.queues.clear();\n this.workers.clear();\n logger.info('All queues and workers closed');\n }\n}\n\nconst queueManager = new QueueManager();\n\nexport const createQueue = (name: string, options?: Partial<QueueOptions>): Queue => {\n return queueManager.createQueue(name, options);\n};\n\nexport const addJob = (\n queueName: string,\n jobName: string,\n data: JobData,\n options?: JobsOptions\n): Promise<Job> => {\n return queueManager.addJob(queueName, jobName, data, options);\n};\n\nexport const processJob = (\n queueName: string,\n processor: JobProcessor,\n options?: WorkerOptions\n): Worker => {\n return queueManager.processJob(queueName, processor, options);\n};\n\nexport const queue = {\n create: createQueue,\n add: addJob,\n process: processJob,\n get: (name: string) => queueManager.getQueue(name),\n getJobCounts: (name: string) => queueManager.getJobCounts(name),\n getJobs: (name: string, start?: number, end?: number) => queueManager.getJobs(name, start, end),\n close: (name: string) => queueManager.closeQueue(name),\n closeAll: () => queueManager.closeAll(),\n setRedisOptions: (options: RedisOptions) => queueManager.setRedisOptions(options),\n};\n\nexport { QueueManager };\nexport default queue;\n"]}
@@ -0,0 +1,293 @@
1
+ import { Queue, Worker } from 'bullmq';
2
+ import { z } from 'zod';
3
+ import pino from 'pino';
4
+
5
+ // src/queue/index.ts
6
+ var envSchema = z.object({
7
+ NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
8
+ PORT: z.string().default("3000"),
9
+ DATABASE_URL: z.string().optional(),
10
+ REDIS_URL: z.string().default("redis://localhost:6379"),
11
+ JWT_SECRET: z.string().min(32).optional(),
12
+ JWT_EXPIRES_IN: z.string().default("7d"),
13
+ JWT_REFRESH_SECRET: z.string().min(32).optional(),
14
+ JWT_REFRESH_EXPIRES_IN: z.string().default("30d"),
15
+ GOOGLE_CLIENT_ID: z.string().optional(),
16
+ GOOGLE_CLIENT_SECRET: z.string().optional(),
17
+ GOOGLE_REDIRECT_URI: z.string().optional(),
18
+ SMTP_HOST: z.string().optional(),
19
+ SMTP_PORT: z.string().default("587"),
20
+ SMTP_USER: z.string().optional(),
21
+ SMTP_PASS: z.string().optional(),
22
+ SMTP_FROM: z.string().optional(),
23
+ TWILIO_ACCOUNT_SID: z.string().optional(),
24
+ TWILIO_AUTH_TOKEN: z.string().optional(),
25
+ TWILIO_PHONE_NUMBER: z.string().optional(),
26
+ SLACK_WEBHOOK_URL: z.string().optional(),
27
+ RATE_LIMIT_WINDOW: z.string().default("1m"),
28
+ RATE_LIMIT_LIMIT: z.string().default("100"),
29
+ LOG_LEVEL: z.enum(["fatal", "error", "warn", "info", "debug", "trace"]).default("info")
30
+ });
31
+ var ConfigManager = class {
32
+ config = null;
33
+ schema;
34
+ validate;
35
+ constructor(options = {}) {
36
+ this.schema = options.schema || envSchema;
37
+ this.validate = options.validate ?? true;
38
+ }
39
+ load() {
40
+ if (this.config) return this.config;
41
+ const env = {};
42
+ for (const key of Object.keys(this.schema.shape)) {
43
+ env[key] = process.env[key];
44
+ }
45
+ if (this.validate) {
46
+ const result = this.schema.safeParse(env);
47
+ if (!result.success) {
48
+ const errors = result.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
49
+ throw new Error(`Config validation failed: ${errors}`);
50
+ }
51
+ this.config = result.data;
52
+ } else {
53
+ this.config = env;
54
+ }
55
+ return this.config;
56
+ }
57
+ get(key) {
58
+ if (!this.config) this.load();
59
+ return this.config[key];
60
+ }
61
+ int(key) {
62
+ const value = this.get(key);
63
+ if (typeof value === "string") return parseInt(value, 10);
64
+ return Number(value);
65
+ }
66
+ bool(key) {
67
+ const value = this.get(key);
68
+ if (typeof value === "boolean") return value;
69
+ if (typeof value === "string") return value.toLowerCase() === "true";
70
+ return Boolean(value);
71
+ }
72
+ isProduction() {
73
+ return this.get("NODE_ENV") === "production";
74
+ }
75
+ isDevelopment() {
76
+ return this.get("NODE_ENV") === "development";
77
+ }
78
+ isTest() {
79
+ return this.get("NODE_ENV") === "test";
80
+ }
81
+ getAll() {
82
+ if (!this.config) this.load();
83
+ return this.config;
84
+ }
85
+ };
86
+ var globalConfig = new ConfigManager();
87
+ var config = {
88
+ load: () => globalConfig.load(),
89
+ get: (key) => globalConfig.get(key),
90
+ int: (key) => globalConfig.int(key),
91
+ bool: (key) => globalConfig.bool(key),
92
+ isProduction: () => globalConfig.isProduction(),
93
+ isDevelopment: () => globalConfig.isDevelopment(),
94
+ isTest: () => globalConfig.isTest(),
95
+ getAll: () => globalConfig.getAll(),
96
+ create: (options) => new ConfigManager(options)
97
+ };
98
+ var LoggerManager = class {
99
+ loggers = /* @__PURE__ */ new Map();
100
+ defaultLogger;
101
+ constructor() {
102
+ const level = config.get("LOG_LEVEL") || "info";
103
+ this.defaultLogger = pino({
104
+ level,
105
+ name: "saas-backend-kit",
106
+ formatters: {
107
+ bindings: (bindings) => ({
108
+ ...bindings,
109
+ service: "saas-backend-kit"
110
+ })
111
+ }
112
+ });
113
+ }
114
+ createLogger(options = {}) {
115
+ const name = options.name || "default";
116
+ if (this.loggers.has(name)) {
117
+ return this.loggers.get(name);
118
+ }
119
+ const level = options.level || config.get("LOG_LEVEL") || "info";
120
+ const logger2 = pino({
121
+ level,
122
+ name: options.name,
123
+ ...options
124
+ });
125
+ this.loggers.set(name, logger2);
126
+ return logger2;
127
+ }
128
+ getLogger(name) {
129
+ if (name) {
130
+ return this.loggers.get(name) || this.defaultLogger;
131
+ }
132
+ return this.defaultLogger;
133
+ }
134
+ child(bindings, options) {
135
+ const name = options?.name || "child";
136
+ const parent = options?.name ? this.getLogger(name) : this.defaultLogger;
137
+ return parent.child(bindings);
138
+ }
139
+ };
140
+ var loggerManager = new LoggerManager();
141
+ var logger = {
142
+ info: (message, ...args) => loggerManager.getLogger().info(message, ...args),
143
+ warn: (message, ...args) => loggerManager.getLogger().warn(message, ...args),
144
+ error: (message, ...args) => loggerManager.getLogger().error(message, ...args),
145
+ debug: (message, ...args) => loggerManager.getLogger().debug(message, ...args),
146
+ trace: (message, ...args) => loggerManager.getLogger().trace(message, ...args),
147
+ fatal: (message, ...args) => loggerManager.getLogger().fatal(message, ...args),
148
+ child: (bindings, options) => loggerManager.child(bindings, options),
149
+ create: (options) => loggerManager.createLogger(options),
150
+ get: (name) => loggerManager.getLogger(name)
151
+ };
152
+
153
+ // src/queue/index.ts
154
+ var QueueManager = class {
155
+ queues = /* @__PURE__ */ new Map();
156
+ workers = /* @__PURE__ */ new Map();
157
+ redisOptions;
158
+ constructor() {
159
+ const redisUrl = config.get("REDIS_URL");
160
+ if (redisUrl) {
161
+ this.redisOptions = { url: redisUrl };
162
+ } else {
163
+ this.redisOptions = {
164
+ host: "localhost",
165
+ port: 6379
166
+ };
167
+ }
168
+ }
169
+ setRedisOptions(options) {
170
+ this.redisOptions = options;
171
+ }
172
+ createQueue(name, options) {
173
+ if (this.queues.has(name)) {
174
+ return this.queues.get(name);
175
+ }
176
+ const queue2 = new Queue(name, {
177
+ connection: this.redisOptions,
178
+ defaultJobOptions: options?.defaultJobOptions || {
179
+ removeOnComplete: 100,
180
+ removeOnFail: 100
181
+ }
182
+ });
183
+ this.queues.set(name, queue2);
184
+ logger.info(`Queue "${name}" created`);
185
+ return queue2;
186
+ }
187
+ getQueue(name) {
188
+ return this.queues.get(name);
189
+ }
190
+ async addJob(queueName, jobName, data, options) {
191
+ const queue2 = this.getQueue(queueName) || this.createQueue(queueName);
192
+ const job = await queue2.add(jobName, data, options);
193
+ logger.debug(`Job "${jobName}" added to queue "${queueName}"`, { jobId: job.id });
194
+ return job;
195
+ }
196
+ async addBulkJobs(queueName, jobs) {
197
+ const queue2 = this.getQueue(queueName) || this.createQueue(queueName);
198
+ const bulkJobs = jobs.map((job) => ({
199
+ name: job.name,
200
+ data: job.data,
201
+ ...job.options
202
+ }));
203
+ const result = await queue2.addBulk(bulkJobs);
204
+ logger.debug(`${jobs.length} jobs added to queue "${queueName}"`);
205
+ return result;
206
+ }
207
+ processJob(queueName, processor, options) {
208
+ this.getQueue(queueName) || this.createQueue(queueName);
209
+ const worker = new Worker(queueName, async (job) => {
210
+ logger.debug(`Processing job "${job.name}"`, { jobId: job.id, queue: queueName });
211
+ return await processor(job);
212
+ }, {
213
+ connection: this.redisOptions,
214
+ concurrency: options?.concurrency || 1,
215
+ ...options
216
+ });
217
+ worker.on("completed", (job) => {
218
+ logger.debug(`Job completed`, { jobId: job.id, queue: queueName });
219
+ });
220
+ worker.on("failed", (job, err) => {
221
+ logger.error(`Job failed`, { jobId: job?.id, queue: queueName, error: err.message });
222
+ });
223
+ worker.on("error", (err) => {
224
+ logger.error(`Worker error`, { queue: queueName, error: err.message });
225
+ });
226
+ this.workers.set(queueName, worker);
227
+ logger.info(`Worker started for queue "${queueName}"`);
228
+ return worker;
229
+ }
230
+ async getJobCounts(queueName) {
231
+ const queue2 = this.getQueue(queueName);
232
+ if (!queue2) {
233
+ return { waiting: 0, active: 0, completed: 0, failed: 0, delayed: 0 };
234
+ }
235
+ return await queue2.getJobCounts();
236
+ }
237
+ async getJobs(queueName, start = 0, end = 10) {
238
+ const queue2 = this.getQueue(queueName);
239
+ if (!queue2) return [];
240
+ return await queue2.getJobs(["waiting", "active", "completed", "failed"], start, end);
241
+ }
242
+ async closeQueue(name) {
243
+ const queue2 = this.queues.get(name);
244
+ if (queue2) {
245
+ await queue2.close();
246
+ this.queues.delete(name);
247
+ logger.info(`Queue "${name}" closed`);
248
+ }
249
+ }
250
+ async closeWorker(name) {
251
+ const worker = this.workers.get(name);
252
+ if (worker) {
253
+ await worker.close();
254
+ this.workers.delete(name);
255
+ logger.info(`Worker for queue "${name}" closed`);
256
+ }
257
+ }
258
+ async closeAll() {
259
+ await Promise.all([
260
+ ...Array.from(this.queues.values()).map((q) => q.close()),
261
+ ...Array.from(this.workers.values()).map((w) => w.close())
262
+ ]);
263
+ this.queues.clear();
264
+ this.workers.clear();
265
+ logger.info("All queues and workers closed");
266
+ }
267
+ };
268
+ var queueManager = new QueueManager();
269
+ var createQueue = (name, options) => {
270
+ return queueManager.createQueue(name, options);
271
+ };
272
+ var addJob = (queueName, jobName, data, options) => {
273
+ return queueManager.addJob(queueName, jobName, data, options);
274
+ };
275
+ var processJob = (queueName, processor, options) => {
276
+ return queueManager.processJob(queueName, processor, options);
277
+ };
278
+ var queue = {
279
+ create: createQueue,
280
+ add: addJob,
281
+ process: processJob,
282
+ get: (name) => queueManager.getQueue(name),
283
+ getJobCounts: (name) => queueManager.getJobCounts(name),
284
+ getJobs: (name, start, end) => queueManager.getJobs(name, start, end),
285
+ close: (name) => queueManager.closeQueue(name),
286
+ closeAll: () => queueManager.closeAll(),
287
+ setRedisOptions: (options) => queueManager.setRedisOptions(options)
288
+ };
289
+ var queue_default = queue;
290
+
291
+ export { QueueManager, addJob, createQueue, queue_default as default, processJob, queue };
292
+ //# sourceMappingURL=index.mjs.map
293
+ //# sourceMappingURL=index.mjs.map