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
package/dist/index.js ADDED
@@ -0,0 +1,1303 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+ var pino = require('pino');
5
+ var bullmq = require('bullmq');
6
+ var jwt = require('jsonwebtoken');
7
+ var bcrypt = require('bcryptjs');
8
+ var nodemailer = require('nodemailer');
9
+ var express = require('express');
10
+
11
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
+
13
+ var pino__default = /*#__PURE__*/_interopDefault(pino);
14
+ var jwt__default = /*#__PURE__*/_interopDefault(jwt);
15
+ var bcrypt__default = /*#__PURE__*/_interopDefault(bcrypt);
16
+ var nodemailer__default = /*#__PURE__*/_interopDefault(nodemailer);
17
+
18
+ var __defProp = Object.defineProperty;
19
+ var __getOwnPropNames = Object.getOwnPropertyNames;
20
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
21
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
22
+ }) : x)(function(x) {
23
+ if (typeof require !== "undefined") return require.apply(this, arguments);
24
+ throw Error('Dynamic require of "' + x + '" is not supported');
25
+ });
26
+ var __esm = (fn, res) => function __init() {
27
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
28
+ };
29
+ var __export = (target, all) => {
30
+ for (var name in all)
31
+ __defProp(target, name, { get: all[name], enumerable: true });
32
+ };
33
+
34
+ // src/auth/types.ts
35
+ var DEFAULT_PERMISSIONS;
36
+ var init_types = __esm({
37
+ "src/auth/types.ts"() {
38
+ DEFAULT_PERMISSIONS = {
39
+ admin: ["*"],
40
+ user: ["read", "write:own"],
41
+ guest: ["read:public"]
42
+ };
43
+ }
44
+ });
45
+ var envSchema, ConfigManager, globalConfig; exports.config = void 0;
46
+ var init_config = __esm({
47
+ "src/config/index.ts"() {
48
+ envSchema = zod.z.object({
49
+ NODE_ENV: zod.z.enum(["development", "production", "test"]).default("development"),
50
+ PORT: zod.z.string().default("3000"),
51
+ DATABASE_URL: zod.z.string().optional(),
52
+ REDIS_URL: zod.z.string().default("redis://localhost:6379"),
53
+ JWT_SECRET: zod.z.string().min(32).optional(),
54
+ JWT_EXPIRES_IN: zod.z.string().default("7d"),
55
+ JWT_REFRESH_SECRET: zod.z.string().min(32).optional(),
56
+ JWT_REFRESH_EXPIRES_IN: zod.z.string().default("30d"),
57
+ GOOGLE_CLIENT_ID: zod.z.string().optional(),
58
+ GOOGLE_CLIENT_SECRET: zod.z.string().optional(),
59
+ GOOGLE_REDIRECT_URI: zod.z.string().optional(),
60
+ SMTP_HOST: zod.z.string().optional(),
61
+ SMTP_PORT: zod.z.string().default("587"),
62
+ SMTP_USER: zod.z.string().optional(),
63
+ SMTP_PASS: zod.z.string().optional(),
64
+ SMTP_FROM: zod.z.string().optional(),
65
+ TWILIO_ACCOUNT_SID: zod.z.string().optional(),
66
+ TWILIO_AUTH_TOKEN: zod.z.string().optional(),
67
+ TWILIO_PHONE_NUMBER: zod.z.string().optional(),
68
+ SLACK_WEBHOOK_URL: zod.z.string().optional(),
69
+ RATE_LIMIT_WINDOW: zod.z.string().default("1m"),
70
+ RATE_LIMIT_LIMIT: zod.z.string().default("100"),
71
+ LOG_LEVEL: zod.z.enum(["fatal", "error", "warn", "info", "debug", "trace"]).default("info")
72
+ });
73
+ ConfigManager = class {
74
+ config = null;
75
+ schema;
76
+ validate;
77
+ constructor(options = {}) {
78
+ this.schema = options.schema || envSchema;
79
+ this.validate = options.validate ?? true;
80
+ }
81
+ load() {
82
+ if (this.config) return this.config;
83
+ const env = {};
84
+ for (const key of Object.keys(this.schema.shape)) {
85
+ env[key] = process.env[key];
86
+ }
87
+ if (this.validate) {
88
+ const result = this.schema.safeParse(env);
89
+ if (!result.success) {
90
+ const errors = result.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
91
+ throw new Error(`Config validation failed: ${errors}`);
92
+ }
93
+ this.config = result.data;
94
+ } else {
95
+ this.config = env;
96
+ }
97
+ return this.config;
98
+ }
99
+ get(key) {
100
+ if (!this.config) this.load();
101
+ return this.config[key];
102
+ }
103
+ int(key) {
104
+ const value = this.get(key);
105
+ if (typeof value === "string") return parseInt(value, 10);
106
+ return Number(value);
107
+ }
108
+ bool(key) {
109
+ const value = this.get(key);
110
+ if (typeof value === "boolean") return value;
111
+ if (typeof value === "string") return value.toLowerCase() === "true";
112
+ return Boolean(value);
113
+ }
114
+ isProduction() {
115
+ return this.get("NODE_ENV") === "production";
116
+ }
117
+ isDevelopment() {
118
+ return this.get("NODE_ENV") === "development";
119
+ }
120
+ isTest() {
121
+ return this.get("NODE_ENV") === "test";
122
+ }
123
+ getAll() {
124
+ if (!this.config) this.load();
125
+ return this.config;
126
+ }
127
+ };
128
+ globalConfig = new ConfigManager();
129
+ exports.config = {
130
+ load: () => globalConfig.load(),
131
+ get: (key) => globalConfig.get(key),
132
+ int: (key) => globalConfig.int(key),
133
+ bool: (key) => globalConfig.bool(key),
134
+ isProduction: () => globalConfig.isProduction(),
135
+ isDevelopment: () => globalConfig.isDevelopment(),
136
+ isTest: () => globalConfig.isTest(),
137
+ getAll: () => globalConfig.getAll(),
138
+ create: (options) => new ConfigManager(options)
139
+ };
140
+ }
141
+ });
142
+
143
+ // src/auth/rbac.ts
144
+ var RBACService, rbacService;
145
+ var init_rbac = __esm({
146
+ "src/auth/rbac.ts"() {
147
+ init_types();
148
+ RBACService = class {
149
+ permissions;
150
+ constructor(permissions = DEFAULT_PERMISSIONS) {
151
+ this.permissions = permissions;
152
+ }
153
+ setPermissions(permissions) {
154
+ this.permissions = permissions;
155
+ }
156
+ addPermission(role, permission) {
157
+ if (!this.permissions[role]) {
158
+ this.permissions[role] = [];
159
+ }
160
+ if (!this.permissions[role].includes(permission)) {
161
+ this.permissions[role].push(permission);
162
+ }
163
+ }
164
+ removePermission(role, permission) {
165
+ if (this.permissions[role]) {
166
+ this.permissions[role] = this.permissions[role].filter((p) => p !== permission);
167
+ }
168
+ }
169
+ getPermissions(role) {
170
+ return this.permissions[role] || [];
171
+ }
172
+ hasPermission(role, requiredPermission) {
173
+ const rolePermissions = this.getPermissions(role);
174
+ if (rolePermissions.includes("*")) {
175
+ return true;
176
+ }
177
+ if (rolePermissions.includes(requiredPermission)) {
178
+ return true;
179
+ }
180
+ const [requiredAction, requiredScope] = requiredPermission.split(":");
181
+ for (const perm of rolePermissions) {
182
+ const [action, scope] = perm.split(":");
183
+ if (action === "*" && (scope === "*" || scope === requiredScope)) {
184
+ return true;
185
+ }
186
+ if (action === requiredAction && (scope === "*" || scope === requiredScope)) {
187
+ return true;
188
+ }
189
+ }
190
+ return false;
191
+ }
192
+ hasRole(userRole, requiredRole) {
193
+ const hierarchy = ["guest", "user", "admin"];
194
+ const userIndex = hierarchy.indexOf(userRole);
195
+ const requiredIndex = hierarchy.indexOf(requiredRole);
196
+ if (userIndex === -1 || requiredIndex === -1) {
197
+ return userRole === requiredRole;
198
+ }
199
+ return userIndex >= requiredIndex;
200
+ }
201
+ authorize(permission) {
202
+ return (role) => this.hasPermission(role, permission);
203
+ }
204
+ authorizeRole(requiredRole) {
205
+ return (role) => this.hasRole(role, requiredRole);
206
+ }
207
+ };
208
+ rbacService = new RBACService();
209
+ }
210
+ });
211
+ var LoggerManager, loggerManager; exports.logger = void 0;
212
+ var init_logger = __esm({
213
+ "src/logger/index.ts"() {
214
+ init_config();
215
+ LoggerManager = class {
216
+ loggers = /* @__PURE__ */ new Map();
217
+ defaultLogger;
218
+ constructor() {
219
+ const level = exports.config.get("LOG_LEVEL") || "info";
220
+ this.defaultLogger = pino__default.default({
221
+ level,
222
+ name: "saas-backend-kit",
223
+ formatters: {
224
+ bindings: (bindings) => ({
225
+ ...bindings,
226
+ service: "saas-backend-kit"
227
+ })
228
+ }
229
+ });
230
+ }
231
+ createLogger(options = {}) {
232
+ const name = options.name || "default";
233
+ if (this.loggers.has(name)) {
234
+ return this.loggers.get(name);
235
+ }
236
+ const level = options.level || exports.config.get("LOG_LEVEL") || "info";
237
+ const logger2 = pino__default.default({
238
+ level,
239
+ name: options.name,
240
+ ...options
241
+ });
242
+ this.loggers.set(name, logger2);
243
+ return logger2;
244
+ }
245
+ getLogger(name) {
246
+ if (name) {
247
+ return this.loggers.get(name) || this.defaultLogger;
248
+ }
249
+ return this.defaultLogger;
250
+ }
251
+ child(bindings, options) {
252
+ const name = options?.name || "child";
253
+ const parent = options?.name ? this.getLogger(name) : this.defaultLogger;
254
+ return parent.child(bindings);
255
+ }
256
+ };
257
+ loggerManager = new LoggerManager();
258
+ exports.logger = {
259
+ info: (message, ...args) => loggerManager.getLogger().info(message, ...args),
260
+ warn: (message, ...args) => loggerManager.getLogger().warn(message, ...args),
261
+ error: (message, ...args) => loggerManager.getLogger().error(message, ...args),
262
+ debug: (message, ...args) => loggerManager.getLogger().debug(message, ...args),
263
+ trace: (message, ...args) => loggerManager.getLogger().trace(message, ...args),
264
+ fatal: (message, ...args) => loggerManager.getLogger().fatal(message, ...args),
265
+ child: (bindings, options) => loggerManager.child(bindings, options),
266
+ create: (options) => loggerManager.createLogger(options),
267
+ get: (name) => loggerManager.getLogger(name)
268
+ };
269
+ }
270
+ });
271
+
272
+ // src/queue/index.ts
273
+ var queue_exports = {};
274
+ __export(queue_exports, {
275
+ QueueManager: () => exports.QueueManager,
276
+ addJob: () => addJob,
277
+ createQueue: () => exports.createQueue,
278
+ default: () => queue_default,
279
+ processJob: () => processJob,
280
+ queue: () => exports.queue
281
+ });
282
+ exports.QueueManager = void 0; var queueManager; exports.createQueue = void 0; var addJob, processJob; exports.queue = void 0; var queue_default;
283
+ var init_queue = __esm({
284
+ "src/queue/index.ts"() {
285
+ init_config();
286
+ init_logger();
287
+ exports.QueueManager = class {
288
+ queues = /* @__PURE__ */ new Map();
289
+ workers = /* @__PURE__ */ new Map();
290
+ redisOptions;
291
+ constructor() {
292
+ const redisUrl = exports.config.get("REDIS_URL");
293
+ if (redisUrl) {
294
+ this.redisOptions = { url: redisUrl };
295
+ } else {
296
+ this.redisOptions = {
297
+ host: "localhost",
298
+ port: 6379
299
+ };
300
+ }
301
+ }
302
+ setRedisOptions(options) {
303
+ this.redisOptions = options;
304
+ }
305
+ createQueue(name, options) {
306
+ if (this.queues.has(name)) {
307
+ return this.queues.get(name);
308
+ }
309
+ const queue2 = new bullmq.Queue(name, {
310
+ connection: this.redisOptions,
311
+ defaultJobOptions: options?.defaultJobOptions || {
312
+ removeOnComplete: 100,
313
+ removeOnFail: 100
314
+ }
315
+ });
316
+ this.queues.set(name, queue2);
317
+ exports.logger.info(`Queue "${name}" created`);
318
+ return queue2;
319
+ }
320
+ getQueue(name) {
321
+ return this.queues.get(name);
322
+ }
323
+ async addJob(queueName, jobName, data, options) {
324
+ const queue2 = this.getQueue(queueName) || this.createQueue(queueName);
325
+ const job = await queue2.add(jobName, data, options);
326
+ exports.logger.debug(`Job "${jobName}" added to queue "${queueName}"`, { jobId: job.id });
327
+ return job;
328
+ }
329
+ async addBulkJobs(queueName, jobs) {
330
+ const queue2 = this.getQueue(queueName) || this.createQueue(queueName);
331
+ const bulkJobs = jobs.map((job) => ({
332
+ name: job.name,
333
+ data: job.data,
334
+ ...job.options
335
+ }));
336
+ const result = await queue2.addBulk(bulkJobs);
337
+ exports.logger.debug(`${jobs.length} jobs added to queue "${queueName}"`);
338
+ return result;
339
+ }
340
+ processJob(queueName, processor, options) {
341
+ this.getQueue(queueName) || this.createQueue(queueName);
342
+ const worker = new bullmq.Worker(queueName, async (job) => {
343
+ exports.logger.debug(`Processing job "${job.name}"`, { jobId: job.id, queue: queueName });
344
+ return await processor(job);
345
+ }, {
346
+ connection: this.redisOptions,
347
+ concurrency: options?.concurrency || 1,
348
+ ...options
349
+ });
350
+ worker.on("completed", (job) => {
351
+ exports.logger.debug(`Job completed`, { jobId: job.id, queue: queueName });
352
+ });
353
+ worker.on("failed", (job, err) => {
354
+ exports.logger.error(`Job failed`, { jobId: job?.id, queue: queueName, error: err.message });
355
+ });
356
+ worker.on("error", (err) => {
357
+ exports.logger.error(`Worker error`, { queue: queueName, error: err.message });
358
+ });
359
+ this.workers.set(queueName, worker);
360
+ exports.logger.info(`Worker started for queue "${queueName}"`);
361
+ return worker;
362
+ }
363
+ async getJobCounts(queueName) {
364
+ const queue2 = this.getQueue(queueName);
365
+ if (!queue2) {
366
+ return { waiting: 0, active: 0, completed: 0, failed: 0, delayed: 0 };
367
+ }
368
+ return await queue2.getJobCounts();
369
+ }
370
+ async getJobs(queueName, start = 0, end = 10) {
371
+ const queue2 = this.getQueue(queueName);
372
+ if (!queue2) return [];
373
+ return await queue2.getJobs(["waiting", "active", "completed", "failed"], start, end);
374
+ }
375
+ async closeQueue(name) {
376
+ const queue2 = this.queues.get(name);
377
+ if (queue2) {
378
+ await queue2.close();
379
+ this.queues.delete(name);
380
+ exports.logger.info(`Queue "${name}" closed`);
381
+ }
382
+ }
383
+ async closeWorker(name) {
384
+ const worker = this.workers.get(name);
385
+ if (worker) {
386
+ await worker.close();
387
+ this.workers.delete(name);
388
+ exports.logger.info(`Worker for queue "${name}" closed`);
389
+ }
390
+ }
391
+ async closeAll() {
392
+ await Promise.all([
393
+ ...Array.from(this.queues.values()).map((q) => q.close()),
394
+ ...Array.from(this.workers.values()).map((w) => w.close())
395
+ ]);
396
+ this.queues.clear();
397
+ this.workers.clear();
398
+ exports.logger.info("All queues and workers closed");
399
+ }
400
+ };
401
+ queueManager = new exports.QueueManager();
402
+ exports.createQueue = (name, options) => {
403
+ return queueManager.createQueue(name, options);
404
+ };
405
+ addJob = (queueName, jobName, data, options) => {
406
+ return queueManager.addJob(queueName, jobName, data, options);
407
+ };
408
+ processJob = (queueName, processor, options) => {
409
+ return queueManager.processJob(queueName, processor, options);
410
+ };
411
+ exports.queue = {
412
+ create: exports.createQueue,
413
+ add: addJob,
414
+ process: processJob,
415
+ get: (name) => queueManager.getQueue(name),
416
+ getJobCounts: (name) => queueManager.getJobCounts(name),
417
+ getJobs: (name, start, end) => queueManager.getJobs(name, start, end),
418
+ close: (name) => queueManager.closeQueue(name),
419
+ closeAll: () => queueManager.closeAll(),
420
+ setRedisOptions: (options) => queueManager.setRedisOptions(options)
421
+ };
422
+ queue_default = exports.queue;
423
+ }
424
+ });
425
+
426
+ // src/rate-limit/express.ts
427
+ function rateLimit(options = {}) {
428
+ const limiter = new RateLimiter(options);
429
+ return limiter.middleware();
430
+ }
431
+ function createRateLimiter(options) {
432
+ return new RateLimiter(options);
433
+ }
434
+ var InMemoryRateLimiter, RateLimiter;
435
+ var init_express = __esm({
436
+ "src/rate-limit/express.ts"() {
437
+ init_config();
438
+ InMemoryRateLimiter = class {
439
+ store = /* @__PURE__ */ new Map();
440
+ cleanupInterval;
441
+ constructor() {
442
+ this.cleanupInterval = setInterval(() => this.cleanup(), 6e4);
443
+ }
444
+ cleanup() {
445
+ const now = Date.now();
446
+ for (const [key, record] of this.store.entries()) {
447
+ if (record.resetTime < now) {
448
+ this.store.delete(key);
449
+ }
450
+ }
451
+ }
452
+ increment(key, windowMs) {
453
+ const now = Date.now();
454
+ const record = this.store.get(key);
455
+ if (!record || record.resetTime < now) {
456
+ const resetTime = now + windowMs;
457
+ this.store.set(key, { count: 1, resetTime });
458
+ return { count: 1, resetTime };
459
+ }
460
+ record.count++;
461
+ return record;
462
+ }
463
+ get(key) {
464
+ return this.store.get(key);
465
+ }
466
+ destroy() {
467
+ clearInterval(this.cleanupInterval);
468
+ }
469
+ };
470
+ RateLimiter = class {
471
+ options;
472
+ store;
473
+ constructor(options = {}) {
474
+ const defaultWindow = exports.config.get("RATE_LIMIT_WINDOW") || "1m";
475
+ const defaultLimit = parseInt(exports.config.get("RATE_LIMIT_LIMIT") || "100", 10);
476
+ this.options = {
477
+ window: options.window || defaultWindow,
478
+ limit: options.limit || defaultLimit,
479
+ keyGenerator: options.keyGenerator || ((req) => req.ip || "unknown"),
480
+ handler: options.handler || this.defaultHandler,
481
+ skipSuccessfulRequests: options.skipSuccessfulRequests || false,
482
+ skipFailedRequests: options.skipFailedRequests || false,
483
+ skip: options.skip || (() => false)
484
+ };
485
+ this.store = new InMemoryRateLimiter();
486
+ }
487
+ getWindowMs() {
488
+ const window = this.options.window;
489
+ const match = window.match(/^(\d+)(s|m|h|d)$/);
490
+ if (!match) return 6e4;
491
+ const value = parseInt(match[1], 10);
492
+ const unit = match[2];
493
+ switch (unit) {
494
+ case "s":
495
+ return value * 1e3;
496
+ case "m":
497
+ return value * 60 * 1e3;
498
+ case "h":
499
+ return value * 60 * 60 * 1e3;
500
+ case "d":
501
+ return value * 24 * 60 * 60 * 1e3;
502
+ default:
503
+ return 6e4;
504
+ }
505
+ }
506
+ defaultHandler(req, res) {
507
+ res.status(429).json({
508
+ error: "Too many requests",
509
+ message: `Rate limit exceeded. Please try again later.`
510
+ });
511
+ }
512
+ middleware() {
513
+ const windowMs = this.getWindowMs();
514
+ return (req, res, next) => {
515
+ if (this.options.skip(req)) {
516
+ return next();
517
+ }
518
+ const key = this.options.keyGenerator(req);
519
+ const record = this.store.increment(key, windowMs);
520
+ const remaining = Math.max(0, this.options.limit - record.count);
521
+ new Date(record.resetTime);
522
+ res.setHeader("X-RateLimit-Limit", this.options.limit);
523
+ res.setHeader("X-RateLimit-Remaining", remaining);
524
+ res.setHeader("X-RateLimit-Reset", Math.ceil(record.resetTime / 1e3));
525
+ if (record.count > this.options.limit) {
526
+ return this.options.handler(req, res);
527
+ }
528
+ next();
529
+ };
530
+ }
531
+ destroy() {
532
+ this.store.destroy();
533
+ }
534
+ };
535
+ }
536
+ });
537
+
538
+ // src/auth/index.ts
539
+ init_types();
540
+
541
+ // src/auth/jwt.ts
542
+ init_config();
543
+ var JWTService = class {
544
+ secret;
545
+ expiresIn;
546
+ refreshSecret;
547
+ refreshExpiresIn;
548
+ constructor(options = {}) {
549
+ this.secret = options.jwtSecret || exports.config.get("JWT_SECRET") || "default-secret-change-in-production";
550
+ this.expiresIn = options.jwtExpiresIn || exports.config.get("JWT_EXPIRES_IN") || "7d";
551
+ this.refreshSecret = options.refreshSecret || exports.config.get("JWT_REFRESH_SECRET") || this.secret;
552
+ this.refreshExpiresIn = options.refreshExpiresIn || exports.config.get("JWT_REFRESH_EXPIRES_IN") || "30d";
553
+ }
554
+ generateToken(payload) {
555
+ return jwt__default.default.sign(payload, this.secret, { expiresIn: this.expiresIn });
556
+ }
557
+ generateRefreshToken(payload) {
558
+ return jwt__default.default.sign(payload, this.refreshSecret, { expiresIn: this.refreshExpiresIn });
559
+ }
560
+ generateTokenPair(payload) {
561
+ return {
562
+ accessToken: this.generateToken(payload),
563
+ refreshToken: this.generateRefreshToken(payload)
564
+ };
565
+ }
566
+ verifyToken(token) {
567
+ return jwt__default.default.verify(token, this.secret);
568
+ }
569
+ verifyRefreshToken(token) {
570
+ return jwt__default.default.verify(token, this.refreshSecret);
571
+ }
572
+ refreshTokens(refreshToken) {
573
+ const payload = this.verifyRefreshToken(refreshToken);
574
+ return this.generateTokenPair(payload);
575
+ }
576
+ };
577
+ var jwtService = new JWTService();
578
+
579
+ // src/auth/index.ts
580
+ init_rbac();
581
+
582
+ // src/auth/oauth.ts
583
+ init_config();
584
+ var OAuthService = class {
585
+ providers = /* @__PURE__ */ new Map();
586
+ constructor() {
587
+ const googleClientId = exports.config.get("GOOGLE_CLIENT_ID");
588
+ const googleClientSecret = exports.config.get("GOOGLE_CLIENT_SECRET");
589
+ const googleRedirectUri = exports.config.get("GOOGLE_REDIRECT_URI");
590
+ if (googleClientId && googleClientSecret && googleRedirectUri) {
591
+ this.registerProvider("google", {
592
+ name: "google",
593
+ clientId: googleClientId,
594
+ clientSecret: googleClientSecret,
595
+ redirectUri: googleRedirectUri,
596
+ authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
597
+ tokenUrl: "https://oauth2.googleapis.com/token",
598
+ userInfoUrl: "https://www.googleapis.com/oauth2/v2/userinfo"
599
+ });
600
+ }
601
+ }
602
+ registerProvider(name, provider) {
603
+ this.providers.set(name, provider);
604
+ }
605
+ getProvider(name) {
606
+ return this.providers.get(name);
607
+ }
608
+ getAuthorizationUrl(providerName, state) {
609
+ const provider = this.getProvider(providerName);
610
+ if (!provider) {
611
+ throw new Error(`OAuth provider ${providerName} not registered`);
612
+ }
613
+ const params = new URLSearchParams({
614
+ client_id: provider.clientId,
615
+ redirect_uri: provider.redirectUri,
616
+ response_type: "code",
617
+ scope: "openid email profile",
618
+ state: state || ""
619
+ });
620
+ return `${provider.authorizationUrl}?${params.toString()}`;
621
+ }
622
+ async exchangeCode(providerName, code) {
623
+ const provider = this.getProvider(providerName);
624
+ if (!provider) {
625
+ throw new Error(`OAuth provider ${providerName} not registered`);
626
+ }
627
+ const response2 = await fetch(provider.tokenUrl, {
628
+ method: "POST",
629
+ headers: {
630
+ "Content-Type": "application/x-www-form-urlencoded"
631
+ },
632
+ body: new URLSearchParams({
633
+ client_id: provider.clientId,
634
+ client_secret: provider.clientSecret,
635
+ code,
636
+ grant_type: "authorization_code",
637
+ redirect_uri: provider.redirectUri
638
+ })
639
+ });
640
+ if (!response2.ok) {
641
+ const error = await response2.text();
642
+ throw new Error(`OAuth token exchange failed: ${error}`);
643
+ }
644
+ const data = await response2.json();
645
+ return {
646
+ accessToken: data.access_token,
647
+ refreshToken: data.refresh_token
648
+ };
649
+ }
650
+ async getUserInfo(providerName, accessToken) {
651
+ const provider = this.getProvider(providerName);
652
+ if (!provider) {
653
+ throw new Error(`OAuth provider ${providerName} not registered`);
654
+ }
655
+ const response2 = await fetch(provider.userInfoUrl, {
656
+ headers: {
657
+ Authorization: `Bearer ${accessToken}`
658
+ }
659
+ });
660
+ if (!response2.ok) {
661
+ const error = await response2.text();
662
+ throw new Error(`OAuth user info fetch failed: ${error}`);
663
+ }
664
+ const data = await response2.json();
665
+ return {
666
+ id: data.id,
667
+ email: data.email,
668
+ name: data.name,
669
+ picture: data.picture
670
+ };
671
+ }
672
+ };
673
+ var oauthService = new OAuthService();
674
+ init_rbac();
675
+ var InMemoryUserStore = class {
676
+ users = /* @__PURE__ */ new Map();
677
+ async findByEmail(email) {
678
+ for (const user of this.users.values()) {
679
+ if (user.email === email) return user;
680
+ }
681
+ return null;
682
+ }
683
+ async findById(id) {
684
+ return this.users.get(id) || null;
685
+ }
686
+ async create(data) {
687
+ const id = Math.random().toString(36).substr(2, 9);
688
+ const hashedPassword = await bcrypt__default.default.hash(data.password, 10);
689
+ const user = {
690
+ id,
691
+ email: data.email,
692
+ password: hashedPassword,
693
+ role: data.role || "user",
694
+ name: data.name
695
+ };
696
+ this.users.set(id, user);
697
+ return user;
698
+ }
699
+ async update(id, data) {
700
+ const user = this.users.get(id);
701
+ if (!user) throw new Error("User not found");
702
+ const updated = { ...user, ...data };
703
+ this.users.set(id, updated);
704
+ return updated;
705
+ }
706
+ };
707
+ var AuthService = class {
708
+ userStore;
709
+ options;
710
+ initialized = false;
711
+ constructor(options = {}, userStore) {
712
+ this.options = {
713
+ jwtSecret: options.jwtSecret || process.env.JWT_SECRET || "default-secret-change-in-production",
714
+ jwtExpiresIn: options.jwtExpiresIn || "7d",
715
+ refreshSecret: options.refreshSecret || process.env.JWT_REFRESH_SECRET || "default-secret-change-in-production",
716
+ refreshExpiresIn: options.refreshExpiresIn || "30d",
717
+ googleClientId: options.googleClientId || process.env.GOOGLE_CLIENT_ID || "",
718
+ googleClientSecret: options.googleClientSecret || process.env.GOOGLE_CLIENT_SECRET || "",
719
+ googleRedirectUri: options.googleRedirectUri || process.env.GOOGLE_REDIRECT_URI || ""
720
+ };
721
+ this.userStore = userStore || new InMemoryUserStore();
722
+ }
723
+ async initialize() {
724
+ if (this.options.googleClientId && this.options.googleClientSecret && this.options.googleRedirectUri) {
725
+ oauthService.registerProvider("google", {
726
+ name: "google",
727
+ clientId: this.options.googleClientId,
728
+ clientSecret: this.options.googleClientSecret,
729
+ redirectUri: this.options.googleRedirectUri,
730
+ authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
731
+ tokenUrl: "https://oauth2.googleapis.com/token",
732
+ userInfoUrl: "https://www.googleapis.com/oauth2/v2/userinfo"
733
+ });
734
+ }
735
+ this.initialized = true;
736
+ }
737
+ async register(data) {
738
+ const existing = await this.userStore.findByEmail(data.email);
739
+ if (existing) {
740
+ throw new Error("User already exists");
741
+ }
742
+ const user = await this.userStore.create(data);
743
+ const tokens = jwtService.generateTokenPair({
744
+ userId: user.id,
745
+ email: user.email,
746
+ role: user.role
747
+ });
748
+ return { user, tokens };
749
+ }
750
+ async login(credentials) {
751
+ const user = await this.userStore.findByEmail(credentials.email);
752
+ if (!user || !user.password) {
753
+ throw new Error("Invalid credentials");
754
+ }
755
+ const isValid = await bcrypt__default.default.compare(credentials.password, user.password);
756
+ if (!isValid) {
757
+ throw new Error("Invalid credentials");
758
+ }
759
+ const tokens = jwtService.generateTokenPair({
760
+ userId: user.id,
761
+ email: user.email,
762
+ role: user.role
763
+ });
764
+ return { user, tokens };
765
+ }
766
+ async refresh(refreshToken) {
767
+ return jwtService.refreshTokens(refreshToken);
768
+ }
769
+ async getGoogleAuthUrl(state) {
770
+ return oauthService.getAuthorizationUrl("google", state);
771
+ }
772
+ async handleGoogleCallback(code) {
773
+ const { accessToken } = await oauthService.exchangeCode("google", code);
774
+ const userInfo = await oauthService.getUserInfo("google", accessToken);
775
+ let user = await this.userStore.findByEmail(userInfo.email);
776
+ if (!user) {
777
+ user = await this.userStore.create({
778
+ email: userInfo.email,
779
+ password: Math.random().toString(36).substr(2, 20),
780
+ name: userInfo.name,
781
+ role: "user"
782
+ });
783
+ }
784
+ const tokens = jwtService.generateTokenPair({
785
+ userId: user.id,
786
+ email: user.email,
787
+ role: user.role
788
+ });
789
+ return { user, tokens };
790
+ }
791
+ getMiddleware() {
792
+ return (req, res, next) => {
793
+ const authHeader = req.headers.authorization;
794
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
795
+ return res.status(401).json({ error: "No token provided" });
796
+ }
797
+ const token = authHeader.substring(7);
798
+ try {
799
+ const payload = jwtService.verifyToken(token);
800
+ req.user = {
801
+ ...payload,
802
+ id: payload.userId
803
+ };
804
+ next();
805
+ } catch (error) {
806
+ return res.status(401).json({ error: "Invalid token" });
807
+ }
808
+ };
809
+ }
810
+ requireUser() {
811
+ return (req, res, next) => {
812
+ if (!req.user) {
813
+ return res.status(401).json({ error: "Authentication required" });
814
+ }
815
+ next();
816
+ };
817
+ }
818
+ requireRole(role) {
819
+ return (req, res, next) => {
820
+ if (!req.user) {
821
+ return res.status(401).json({ error: "Authentication required" });
822
+ }
823
+ if (!rbacService.hasRole(req.user.role, role)) {
824
+ return res.status(403).json({ error: "Insufficient permissions" });
825
+ }
826
+ next();
827
+ };
828
+ }
829
+ requirePermission(permission) {
830
+ return (req, res, next) => {
831
+ if (!req.user) {
832
+ return res.status(401).json({ error: "Authentication required" });
833
+ }
834
+ if (!rbacService.hasPermission(req.user.role, permission)) {
835
+ return res.status(403).json({ error: "Insufficient permissions" });
836
+ }
837
+ next();
838
+ };
839
+ }
840
+ };
841
+ var authServiceInstance = null;
842
+ function createAuth(options, userStore) {
843
+ authServiceInstance = new AuthService(options, userStore);
844
+ return authServiceInstance;
845
+ }
846
+ function auth() {
847
+ if (!authServiceInstance) {
848
+ authServiceInstance = new AuthService();
849
+ }
850
+ return authServiceInstance;
851
+ }
852
+ var Auth = {
853
+ initialize: (options) => {
854
+ const service = createAuth(options);
855
+ return {
856
+ service,
857
+ getMiddleware: () => service.getMiddleware(),
858
+ requireUser: () => service.requireUser(),
859
+ requireRole: (role) => service.requireRole(role),
860
+ requirePermission: (permission) => service.requirePermission(permission)
861
+ };
862
+ }
863
+ };
864
+
865
+ // src/index.ts
866
+ init_queue();
867
+
868
+ // src/notifications/index.ts
869
+ init_config();
870
+ init_logger();
871
+ var EmailService = class {
872
+ transporter = null;
873
+ from;
874
+ constructor() {
875
+ this.from = exports.config.get("SMTP_FROM") || "noreply@example.com";
876
+ this.initialize();
877
+ }
878
+ initialize() {
879
+ const host = exports.config.get("SMTP_HOST");
880
+ const port = parseInt(exports.config.get("SMTP_PORT") || "587", 10);
881
+ const user = exports.config.get("SMTP_USER");
882
+ const pass = exports.config.get("SMTP_PASS");
883
+ if (host && user && pass) {
884
+ this.transporter = nodemailer__default.default.createTransport({
885
+ host,
886
+ port,
887
+ secure: port === 465,
888
+ auth: {
889
+ user,
890
+ pass
891
+ }
892
+ });
893
+ exports.logger.info("Email service initialized");
894
+ }
895
+ }
896
+ setTransporter(transporter) {
897
+ this.transporter = transporter;
898
+ }
899
+ async send(options) {
900
+ if (!this.transporter) {
901
+ exports.logger.warn("Email transporter not configured, skipping email");
902
+ return { messageId: "mock-message-id" };
903
+ }
904
+ const mailOptions = {
905
+ from: options.from || this.from,
906
+ to: Array.isArray(options.to) ? options.to.join(", ") : options.to,
907
+ subject: options.subject,
908
+ text: options.text,
909
+ html: options.html || this.renderTemplate(options.template, options.templateData),
910
+ cc: options.cc,
911
+ bcc: options.bcc,
912
+ attachments: options.attachments
913
+ };
914
+ const result = await this.transporter.sendMail(mailOptions);
915
+ exports.logger.info(`Email sent to ${options.to}`, { messageId: result.messageId });
916
+ return { messageId: result.messageId };
917
+ }
918
+ renderTemplate(templateName, data) {
919
+ if (!templateName || !data) return void 0;
920
+ const templates = {
921
+ welcome: (data2) => `
922
+ <h1>Welcome, ${data2.name || "User"}!</h1>
923
+ <p>Thank you for joining ${data2.appName || "our platform"}.</p>
924
+ <p>Get started by verifying your email.</p>
925
+ `,
926
+ passwordReset: (data2) => `
927
+ <h1>Password Reset</h1>
928
+ <p>Click <a href="${data2.resetUrl}">here</a> to reset your password.</p>
929
+ <p>This link expires in ${data2.expiry || "1 hour"}.</p>
930
+ `,
931
+ verification: (data2) => `
932
+ <h1>Verify Your Email</h1>
933
+ <p>Click <a href="${data2.verifyUrl}">here</a> to verify your email.</p>
934
+ `
935
+ };
936
+ const template = templates[templateName];
937
+ return template ? template(data) : void 0;
938
+ }
939
+ };
940
+ var SMSService = class {
941
+ accountSid;
942
+ authToken;
943
+ phoneNumber;
944
+ initialized = false;
945
+ constructor() {
946
+ this.accountSid = exports.config.get("TWILIO_ACCOUNT_SID") || "";
947
+ this.authToken = exports.config.get("TWILIO_AUTH_TOKEN") || "";
948
+ this.phoneNumber = exports.config.get("TWILIO_PHONE_NUMBER") || "";
949
+ this.initialized = !!(this.accountSid && this.authToken && this.phoneNumber);
950
+ }
951
+ async send(options) {
952
+ if (!this.initialized) {
953
+ exports.logger.warn("Twilio not configured, skipping SMS");
954
+ return { sid: "mock-sid" };
955
+ }
956
+ try {
957
+ const twilio = await import('twilio');
958
+ const client = twilio.default(this.accountSid, this.authToken);
959
+ const result = await client.messages.create({
960
+ body: options.message,
961
+ from: options.from || this.phoneNumber,
962
+ to: options.to
963
+ });
964
+ exports.logger.info(`SMS sent to ${options.to}`, { sid: result.sid });
965
+ return { sid: result.sid };
966
+ } catch (error) {
967
+ exports.logger.error("Failed to send SMS", { error });
968
+ throw error;
969
+ }
970
+ }
971
+ };
972
+ var WebhookService = class {
973
+ async send(options) {
974
+ const response2 = await fetch(options.url, {
975
+ method: options.method || "POST",
976
+ headers: {
977
+ "Content-Type": "application/json",
978
+ ...options.headers
979
+ },
980
+ body: options.body ? JSON.stringify(options.body) : void 0,
981
+ signal: options.timeout ? AbortSignal.timeout(options.timeout) : void 0
982
+ });
983
+ const body = await response2.json().catch(() => null);
984
+ exports.logger.info(`Webhook sent to ${options.url}`, { status: response2.status });
985
+ return { status: response2.status, body };
986
+ }
987
+ };
988
+ var SlackService = class {
989
+ webhookUrl;
990
+ constructor() {
991
+ this.webhookUrl = exports.config.get("SLACK_WEBHOOK_URL") || "";
992
+ }
993
+ setWebhookUrl(url) {
994
+ this.webhookUrl = url;
995
+ }
996
+ async send(options) {
997
+ if (!this.webhookUrl) {
998
+ exports.logger.warn("Slack webhook not configured, skipping message");
999
+ return { ok: false };
1000
+ }
1001
+ const payload = {
1002
+ text: options.text,
1003
+ blocks: options.blocks,
1004
+ channel: options.channel,
1005
+ username: options.username,
1006
+ icon_emoji: options.iconEmoji
1007
+ };
1008
+ const response2 = await fetch(this.webhookUrl, {
1009
+ method: "POST",
1010
+ headers: { "Content-Type": "application/json" },
1011
+ body: JSON.stringify(payload)
1012
+ });
1013
+ const ok = response2.ok;
1014
+ if (ok) {
1015
+ exports.logger.info("Slack message sent");
1016
+ } else {
1017
+ exports.logger.error("Failed to send Slack message");
1018
+ }
1019
+ return { ok };
1020
+ }
1021
+ };
1022
+ var NotificationService = class {
1023
+ email;
1024
+ sms;
1025
+ webhook;
1026
+ slack;
1027
+ constructor() {
1028
+ this.email = new EmailService();
1029
+ this.sms = new SMSService();
1030
+ this.webhook = new WebhookService();
1031
+ this.slack = new SlackService();
1032
+ }
1033
+ };
1034
+ var notify = new NotificationService();
1035
+ var notification = {
1036
+ email: (options) => notify.email.send(options),
1037
+ sms: (options) => notify.sms.send(options),
1038
+ webhook: (options) => notify.webhook.send(options),
1039
+ slack: (options) => notify.slack.send(options)
1040
+ };
1041
+
1042
+ // src/index.ts
1043
+ init_logger();
1044
+
1045
+ // src/rate-limit/index.ts
1046
+ init_express();
1047
+
1048
+ // src/index.ts
1049
+ init_config();
1050
+ var ResponseHelper = class {
1051
+ static success(res, data, message, statusCode = 200) {
1052
+ const response2 = {
1053
+ success: true,
1054
+ data,
1055
+ message
1056
+ };
1057
+ return res.status(statusCode).json(response2);
1058
+ }
1059
+ static created(res, data, message = "Resource created") {
1060
+ return this.success(res, data, message, 201);
1061
+ }
1062
+ static updated(res, data, message = "Resource updated") {
1063
+ return this.success(res, data, message, 200);
1064
+ }
1065
+ static deleted(res, message = "Resource deleted") {
1066
+ return this.success(res, null, message, 200);
1067
+ }
1068
+ static error(res, error, statusCode = 400, code, details) {
1069
+ const response2 = {
1070
+ success: false,
1071
+ error,
1072
+ code,
1073
+ ...details && { details }
1074
+ };
1075
+ return res.status(statusCode).json(response2);
1076
+ }
1077
+ static badRequest(res, error = "Bad request", code) {
1078
+ return this.error(res, error, 400, code);
1079
+ }
1080
+ static unauthorized(res, error = "Unauthorized", code) {
1081
+ return this.error(res, error, 401, code);
1082
+ }
1083
+ static forbidden(res, error = "Forbidden", code) {
1084
+ return this.error(res, error, 403, code);
1085
+ }
1086
+ static notFound(res, error = "Resource not found", code) {
1087
+ return this.error(res, error, 404, code);
1088
+ }
1089
+ static conflict(res, error = "Conflict", code) {
1090
+ return this.error(res, error, 409, code);
1091
+ }
1092
+ static validationError(res, error, details) {
1093
+ return this.error(res, error, 422, "VALIDATION_ERROR", details);
1094
+ }
1095
+ static internalError(res, error = "Internal server error") {
1096
+ return this.error(res, error, 500, "INTERNAL_ERROR");
1097
+ }
1098
+ static paginated(res, data, page, limit, total) {
1099
+ const totalPages = Math.ceil(total / limit);
1100
+ const response2 = {
1101
+ success: true,
1102
+ data,
1103
+ meta: {
1104
+ page,
1105
+ limit,
1106
+ total,
1107
+ totalPages
1108
+ }
1109
+ };
1110
+ return res.status(200).json(response2);
1111
+ }
1112
+ static noContent(res) {
1113
+ return res.status(204).send();
1114
+ }
1115
+ };
1116
+ express.Response.prototype.success = function(data, message, statusCode = 200) {
1117
+ return ResponseHelper.success(this, data, message, statusCode);
1118
+ };
1119
+ express.Response.prototype.created = function(data, message) {
1120
+ return ResponseHelper.created(this, data, message);
1121
+ };
1122
+ express.Response.prototype.updated = function(data, message) {
1123
+ return ResponseHelper.updated(this, data, message);
1124
+ };
1125
+ express.Response.prototype.deleted = function(message) {
1126
+ return ResponseHelper.deleted(this, message);
1127
+ };
1128
+ express.Response.prototype.error = function(error, statusCode = 400, code, details) {
1129
+ return ResponseHelper.error(this, error, statusCode, code, details);
1130
+ };
1131
+ express.Response.prototype.badRequest = function(error, code) {
1132
+ return ResponseHelper.badRequest(this, error, code);
1133
+ };
1134
+ express.Response.prototype.unauthorized = function(error, code) {
1135
+ return ResponseHelper.unauthorized(this, error, code);
1136
+ };
1137
+ express.Response.prototype.forbidden = function(error, code) {
1138
+ return ResponseHelper.forbidden(this, error, code);
1139
+ };
1140
+ express.Response.prototype.notFound = function(error, code) {
1141
+ return ResponseHelper.notFound(this, error, code);
1142
+ };
1143
+ express.Response.prototype.conflict = function(error, code) {
1144
+ return ResponseHelper.conflict(this, error, code);
1145
+ };
1146
+ express.Response.prototype.validationError = function(error, details) {
1147
+ return ResponseHelper.validationError(this, error, details);
1148
+ };
1149
+ express.Response.prototype.internalError = function(error) {
1150
+ return ResponseHelper.internalError(this, error);
1151
+ };
1152
+ express.Response.prototype.paginated = function(data, page, limit, total) {
1153
+ return ResponseHelper.paginated(this, data, page, limit, total);
1154
+ };
1155
+ var response = ResponseHelper;
1156
+
1157
+ // src/plugin.ts
1158
+ init_logger();
1159
+ init_config();
1160
+ var PluginManager = class {
1161
+ plugins = /* @__PURE__ */ new Map();
1162
+ app = null;
1163
+ authService = null;
1164
+ queueManager = null;
1165
+ register(plugin) {
1166
+ this.plugins.set(plugin.name, plugin);
1167
+ exports.logger.info(`Plugin "${plugin.name}" registered`);
1168
+ }
1169
+ unregister(name) {
1170
+ this.plugins.delete(name);
1171
+ exports.logger.info(`Plugin "${name}" unregistered`);
1172
+ }
1173
+ get(name) {
1174
+ return this.plugins.get(name);
1175
+ }
1176
+ getAll() {
1177
+ return Array.from(this.plugins.values());
1178
+ }
1179
+ has(name) {
1180
+ return this.plugins.has(name);
1181
+ }
1182
+ async initializeAll(app) {
1183
+ this.app = app;
1184
+ for (const plugin of this.plugins.values()) {
1185
+ exports.logger.info(`Initializing plugin "${plugin.name}"`);
1186
+ await plugin.initialize(app);
1187
+ if (plugin.middleware && "use" in app) {
1188
+ for (const mw of plugin.middleware) {
1189
+ app.use(mw);
1190
+ }
1191
+ }
1192
+ }
1193
+ }
1194
+ };
1195
+ var SaaSAppBuilder = class {
1196
+ options;
1197
+ pluginManager;
1198
+ initialized = false;
1199
+ constructor(options = {}) {
1200
+ this.options = options;
1201
+ this.pluginManager = new PluginManager();
1202
+ }
1203
+ use(plugin) {
1204
+ this.pluginManager.register(plugin);
1205
+ return this;
1206
+ }
1207
+ async initialize(app) {
1208
+ if (this.initialized) {
1209
+ throw new Error("App already initialized");
1210
+ }
1211
+ exports.config.load();
1212
+ if (this.options.logger !== false) {
1213
+ const loggerOptions = typeof this.options.logger === "object" ? this.options.logger : {};
1214
+ exports.logger.create(loggerOptions);
1215
+ }
1216
+ if (this.options.auth !== false) {
1217
+ const authOptions = typeof this.options.auth === "object" ? this.options.auth : {};
1218
+ this.authService = createAuth(authOptions);
1219
+ await this.authService.initialize();
1220
+ this.pluginManager.register({
1221
+ name: "auth",
1222
+ initialize: (app2) => {
1223
+ if ("use" in app2) {
1224
+ app2.use(this.authService.getMiddleware());
1225
+ }
1226
+ },
1227
+ middleware: [this.authService.getMiddleware()]
1228
+ });
1229
+ }
1230
+ if (this.options.queue !== false) {
1231
+ const queueOptions = typeof this.options.queue === "object" ? this.options.queue : {};
1232
+ if (queueOptions.redisUrl) {
1233
+ (await Promise.resolve().then(() => (init_queue(), queue_exports))).queue.setRedisOptions({ url: queueOptions.redisUrl });
1234
+ }
1235
+ this.pluginManager.register({
1236
+ name: "queue",
1237
+ initialize: () => {
1238
+ }
1239
+ });
1240
+ }
1241
+ if (this.options.rateLimit !== false) {
1242
+ const rateLimitOptions = typeof this.options.rateLimit === "object" ? this.options.rateLimit : {};
1243
+ this.pluginManager.register({
1244
+ name: "rate-limit",
1245
+ initialize: (app2) => {
1246
+ if ("use" in app2) {
1247
+ app2.use(rateLimit(rateLimitOptions));
1248
+ }
1249
+ },
1250
+ middleware: [rateLimit(rateLimitOptions)]
1251
+ });
1252
+ }
1253
+ if (this.options.notifications !== false) {
1254
+ this.pluginManager.register({
1255
+ name: "notifications",
1256
+ initialize: () => {
1257
+ }
1258
+ });
1259
+ }
1260
+ await this.pluginManager.initializeAll(app);
1261
+ this.initialized = true;
1262
+ exports.logger.info("SaaS App initialized");
1263
+ }
1264
+ getAuth() {
1265
+ return this.authService;
1266
+ }
1267
+ getPluginManager() {
1268
+ return this.pluginManager;
1269
+ }
1270
+ };
1271
+ function createApp(options = {}) {
1272
+ return new SaaSAppBuilder(options);
1273
+ }
1274
+ function createExpressApp(options = {}) {
1275
+ const express = __require("express");
1276
+ const app = express();
1277
+ const builder = createApp({ ...options, framework: "express" });
1278
+ builder.initialize(app);
1279
+ return app;
1280
+ }
1281
+
1282
+ // src/index.ts
1283
+ init_types();
1284
+ init_queue();
1285
+ init_logger();
1286
+ init_config();
1287
+
1288
+ exports.Auth = Auth;
1289
+ exports.AuthService = AuthService;
1290
+ exports.PluginManager = PluginManager;
1291
+ exports.ResponseHelper = ResponseHelper;
1292
+ exports.SaaSAppBuilder = SaaSAppBuilder;
1293
+ exports.auth = auth;
1294
+ exports.createApp = createApp;
1295
+ exports.createAuth = createAuth;
1296
+ exports.createExpressApp = createExpressApp;
1297
+ exports.createRateLimiter = createRateLimiter;
1298
+ exports.notification = notification;
1299
+ exports.notify = notify;
1300
+ exports.rateLimit = rateLimit;
1301
+ exports.response = response;
1302
+ //# sourceMappingURL=index.js.map
1303
+ //# sourceMappingURL=index.js.map