@kuckit/infrastructure 1.0.2 → 2.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 (131) hide show
  1. package/dist/apply-decorators-CW23qWy7.d.ts +23 -0
  2. package/dist/apply-decorators-CaHIAL5X.js +29 -0
  3. package/dist/apply-decorators-CaHIAL5X.js.map +1 -0
  4. package/dist/cache/in-memory-cache-store.d.ts +2 -0
  5. package/dist/cache/in-memory-cache-store.js +3 -0
  6. package/dist/cache/index.d.ts +3 -0
  7. package/dist/cache/index.js +4 -0
  8. package/dist/cache-BjdZ-Ye4.js +1 -0
  9. package/dist/core.module-B1TdC0yX.d.ts +17 -0
  10. package/dist/core.module-DMtIkTcz.js +48 -0
  11. package/dist/core.module-DMtIkTcz.js.map +1 -0
  12. package/dist/db/drizzle/db.d.ts +2 -0
  13. package/dist/db/drizzle/db.js +3 -0
  14. package/dist/db/drizzle/repositories/index.d.ts +3 -0
  15. package/dist/db/drizzle/repositories/index.js +4 -0
  16. package/dist/db/drizzle/repositories/user.drizzle.d.ts +2 -0
  17. package/dist/db/drizzle/repositories/user.drizzle.js +3 -0
  18. package/dist/db/transaction.d.ts +2 -0
  19. package/dist/db/transaction.js +4 -0
  20. package/dist/db-C4IcCT04.js +25 -0
  21. package/dist/db-C4IcCT04.js.map +1 -0
  22. package/dist/db-tAPHBDyL.d.ts +17 -0
  23. package/dist/decorators/apply-decorators.d.ts +2 -0
  24. package/dist/decorators/apply-decorators.js +5 -0
  25. package/dist/decorators/index.d.ts +8 -0
  26. package/dist/decorators/index.js +9 -0
  27. package/dist/decorators/use-case-decorator.d.ts +3 -0
  28. package/dist/decorators/use-case-decorator.js +4 -0
  29. package/dist/decorators/with-caching.d.ts +2 -0
  30. package/dist/decorators/with-caching.js +3 -0
  31. package/dist/decorators/with-rate-limit.d.ts +2 -0
  32. package/dist/decorators/with-rate-limit.js +3 -0
  33. package/dist/decorators/with-retry.d.ts +2 -0
  34. package/dist/decorators/with-retry.js +3 -0
  35. package/dist/decorators-CqyPE9AQ.js +1 -0
  36. package/dist/error-handler-BDid7SIZ.d.ts +47 -0
  37. package/dist/error-handler-D4s_TTI1.js +80 -0
  38. package/dist/error-handler-D4s_TTI1.js.map +1 -0
  39. package/dist/errors/error-handler.d.ts +3 -0
  40. package/dist/errors/error-handler.js +4 -0
  41. package/dist/errors/index.d.ts +4 -0
  42. package/dist/errors/index.js +5 -0
  43. package/dist/errors-BB_jeye8.js +43 -0
  44. package/dist/errors-BB_jeye8.js.map +1 -0
  45. package/dist/errors-DfkerzdO.js +1 -0
  46. package/dist/event-publisher-adapter-B02oKEmP.js +46 -0
  47. package/dist/event-publisher-adapter-B02oKEmP.js.map +1 -0
  48. package/dist/event-publisher-adapter-CpxK0OJ3.d.ts +12 -0
  49. package/dist/events/event-publisher-adapter.d.ts +2 -0
  50. package/dist/events/event-publisher-adapter.js +3 -0
  51. package/dist/events/in-memory-event-bus.d.ts +2 -0
  52. package/dist/events/in-memory-event-bus.js +3 -0
  53. package/dist/events/in-memory-event-publisher.d.ts +2 -0
  54. package/dist/events/in-memory-event-publisher.js +3 -0
  55. package/dist/events/index.d.ts +5 -0
  56. package/dist/events/index.js +6 -0
  57. package/dist/events-Dqynhuj2.js +1 -0
  58. package/dist/in-memory-cache-store-BaRxM--K.d.ts +31 -0
  59. package/dist/in-memory-cache-store-oClww-8m.js +72 -0
  60. package/dist/in-memory-cache-store-oClww-8m.js.map +1 -0
  61. package/dist/in-memory-event-bus-BCyPrNAE.js +60 -0
  62. package/dist/in-memory-event-bus-BCyPrNAE.js.map +1 -0
  63. package/dist/in-memory-event-bus-CqIBLRze.d.ts +21 -0
  64. package/dist/in-memory-event-publisher-BdOlxfkx.js +28 -0
  65. package/dist/in-memory-event-publisher-BdOlxfkx.js.map +1 -0
  66. package/dist/in-memory-event-publisher-CxOQ-hnq.d.ts +12 -0
  67. package/dist/in-memory-rate-limiter-BDSHZXxf.js +72 -0
  68. package/dist/in-memory-rate-limiter-BDSHZXxf.js.map +1 -0
  69. package/dist/in-memory-rate-limiter-DJsxdZZR.d.ts +34 -0
  70. package/dist/index-B5F3AfVc.d.ts +1 -0
  71. package/dist/index-B7z6dpFd.d.ts +1 -0
  72. package/dist/index-BH67NKRs.d.ts +2 -0
  73. package/dist/index-C0yeuOwC.d.ts +1 -0
  74. package/dist/index-C6nYd7xV.d.ts +2 -0
  75. package/dist/index-DVGDAddE.d.ts +1 -0
  76. package/dist/index-DXJbbtWQ.d.ts +1 -0
  77. package/dist/index-LKrIp3Oo.d.ts +1 -0
  78. package/dist/index.d.ts +29 -506
  79. package/dist/index.js +27 -969
  80. package/dist/logger-Bl10drB8.d.ts +23 -0
  81. package/dist/logging/index.d.ts +5 -0
  82. package/dist/logging/index.js +5 -0
  83. package/dist/logging/request-logger.d.ts +2 -0
  84. package/dist/logging/request-logger.js +3 -0
  85. package/dist/logging/structured-logger.d.ts +3 -0
  86. package/dist/logging/structured-logger.js +3 -0
  87. package/dist/logging-4mLSrMc6.js +1 -0
  88. package/dist/modules/core.module.d.ts +10 -0
  89. package/dist/modules/core.module.js +13 -0
  90. package/dist/modules/index.d.ts +12 -0
  91. package/dist/modules/index.js +16 -0
  92. package/dist/modules/types.d.ts +9 -0
  93. package/dist/modules/types.js +6 -0
  94. package/dist/modules/user.module.d.ts +10 -0
  95. package/dist/modules/user.module.js +4 -0
  96. package/dist/modules-C_2SF3he.js +1 -0
  97. package/dist/rate-limiter/in-memory-rate-limiter.d.ts +2 -0
  98. package/dist/rate-limiter/in-memory-rate-limiter.js +3 -0
  99. package/dist/rate-limiter/index.d.ts +3 -0
  100. package/dist/rate-limiter/index.js +4 -0
  101. package/dist/rate-limiter-BnvPGJOK.js +1 -0
  102. package/dist/repositories-nTfSJyvW.js +1 -0
  103. package/dist/request-logger-CK3SOnoz.d.ts +23 -0
  104. package/dist/request-logger-Cw1XQWTV.js +49 -0
  105. package/dist/request-logger-Cw1XQWTV.js.map +1 -0
  106. package/dist/structured-logger-BsxDI9zX.js +119 -0
  107. package/dist/structured-logger-BsxDI9zX.js.map +1 -0
  108. package/dist/structured-logger-Dz06Uz-u.d.ts +47 -0
  109. package/dist/transaction-akuz5Fch.d.ts +22 -0
  110. package/dist/transaction-r4Sy3eC-.js +35 -0
  111. package/dist/transaction-r4Sy3eC-.js.map +1 -0
  112. package/dist/types-65aFqB5L.d.ts +62 -0
  113. package/dist/use-case-decorator-DzPSPSv5.d.ts +52 -0
  114. package/dist/use-case-decorator-GmDeYViz.js +118 -0
  115. package/dist/use-case-decorator-GmDeYViz.js.map +1 -0
  116. package/dist/user.drizzle-9kkstnkV.d.ts +12 -0
  117. package/dist/user.drizzle-CgKIqqb3.js +57 -0
  118. package/dist/user.drizzle-CgKIqqb3.js.map +1 -0
  119. package/dist/user.module-BEpCbKsU.js +17 -0
  120. package/dist/user.module-BEpCbKsU.js.map +1 -0
  121. package/dist/user.module-D3lVJ98T.d.ts +15 -0
  122. package/dist/with-caching-BniS1aZd.d.ts +39 -0
  123. package/dist/with-caching-NmBxu7vJ.js +37 -0
  124. package/dist/with-caching-NmBxu7vJ.js.map +1 -0
  125. package/dist/with-rate-limit-Cp2V1RHn.js +48 -0
  126. package/dist/with-rate-limit-Cp2V1RHn.js.map +1 -0
  127. package/dist/with-rate-limit-DK4ZF-Qg.d.ts +45 -0
  128. package/dist/with-retry-B9-hUj7I.d.ts +40 -0
  129. package/dist/with-retry-coyYPiX1.js +49 -0
  130. package/dist/with-retry-coyYPiX1.js.map +1 -0
  131. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -1,971 +1,29 @@
1
- import { drizzle } from "drizzle-orm/node-postgres";
2
- import { Pool } from "pg";
3
- import { eq } from "drizzle-orm";
4
- import { user } from "@kuckit/db/schema/auth";
5
- import { asClass, asFunction, asValue } from "awilix";
6
- import { createWriteStream, existsSync, mkdirSync } from "fs";
7
- import path from "path";
8
- import { randomUUID } from "crypto";
9
- import { AppError, ErrorCode } from "@kuckit/domain";
10
- import { SystemClock } from "@kuckit/domain/ports/clock";
11
- import { google } from "@ai-sdk/google";
12
- import { auth } from "@kuckit/auth";
1
+ import { n as createDbPool, t as createDb } from "./db-C4IcCT04.js";
2
+ import { t as makeDrizzleUserRepository } from "./user.drizzle-CgKIqqb3.js";
3
+ import "./repositories-nTfSJyvW.js";
4
+ import { t as runInTransaction } from "./transaction-r4Sy3eC-.js";
5
+ import { n as makeStructuredLogger, t as StructuredLogger } from "./structured-logger-BsxDI9zX.js";
6
+ import { t as makeRequestLogger } from "./request-logger-Cw1XQWTV.js";
7
+ import "./logging-4mLSrMc6.js";
8
+ import "./errors-BB_jeye8.js";
9
+ import { n as makeErrorHandler, t as ErrorHandler } from "./error-handler-D4s_TTI1.js";
10
+ import "./errors-DfkerzdO.js";
11
+ import { t as InMemoryEventBus } from "./in-memory-event-bus-BCyPrNAE.js";
12
+ import { t as makeEventPublisherAdapter } from "./event-publisher-adapter-B02oKEmP.js";
13
+ import { t as makeInMemoryEventPublisher } from "./in-memory-event-publisher-BdOlxfkx.js";
14
+ import "./events-Dqynhuj2.js";
15
+ import { t as InMemoryCacheStore } from "./in-memory-cache-store-oClww-8m.js";
16
+ import "./cache-BjdZ-Ye4.js";
17
+ import { t as InMemoryRateLimiterStore } from "./in-memory-rate-limiter-BDSHZXxf.js";
18
+ import "./rate-limiter-BnvPGJOK.js";
19
+ import { a as withRequestTracing, i as withPerformanceMonitoring, n as makeDecoratedUseCase, r as withErrorHandling, t as decorateUseCase } from "./use-case-decorator-GmDeYViz.js";
20
+ import { t as applyDecorators } from "./apply-decorators-CaHIAL5X.js";
21
+ import { t as withCaching } from "./with-caching-NmBxu7vJ.js";
22
+ import { t as withRetry } from "./with-retry-coyYPiX1.js";
23
+ import { n as withRateLimit, t as RateLimitError } from "./with-rate-limit-Cp2V1RHn.js";
24
+ import "./decorators-CqyPE9AQ.js";
25
+ import { t as registerCoreModule } from "./core.module-DMtIkTcz.js";
26
+ import { t as registerUserModule } from "./user.module-BEpCbKsU.js";
27
+ import "./modules-C_2SF3he.js";
13
28
 
14
- //#region src/db/drizzle/db.ts
15
- /**
16
- * Create database connection pool
17
- */
18
- const createDbPool = (connectionString) => {
19
- return new Pool({
20
- connectionString,
21
- max: 20,
22
- idleTimeoutMillis: 3e4,
23
- connectionTimeoutMillis: 2e3
24
- });
25
- };
26
- /**
27
- * Create Drizzle instance from pool
28
- */
29
- const createDb = (pool) => {
30
- return drizzle(pool);
31
- };
32
-
33
- //#endregion
34
- //#region src/db/drizzle/repositories/user.drizzle.ts
35
- /**
36
- * Map Drizzle row to domain entity
37
- */
38
- const toDomainUser = (row) => ({
39
- id: row.id,
40
- name: row.name,
41
- email: row.email,
42
- emailVerified: row.emailVerified,
43
- permissions: row.permissions,
44
- createdAt: row.createdAt,
45
- updatedAt: row.updatedAt
46
- });
47
- /**
48
- * Map domain entity to Drizzle insert type
49
- */
50
- const toDbUser = (user$1) => ({
51
- id: user$1.id,
52
- name: user$1.name,
53
- email: user$1.email,
54
- emailVerified: user$1.emailVerified,
55
- permissions: [...user$1.permissions],
56
- createdAt: user$1.createdAt,
57
- updatedAt: user$1.updatedAt
58
- });
59
- /**
60
- * Drizzle implementation of UserRepository
61
- */
62
- const makeDrizzleUserRepository = (db) => ({
63
- async findById(id) {
64
- const rows = await db.select().from(user).where(eq(user.id, id)).limit(1);
65
- return rows[0] ? toDomainUser(rows[0]) : null;
66
- },
67
- async findByEmail(email) {
68
- const rows = await db.select().from(user).where(eq(user.email, email)).limit(1);
69
- return rows[0] ? toDomainUser(rows[0]) : null;
70
- },
71
- async save(user$1) {
72
- await db.insert(user).values(toDbUser(user$1)).onConflictDoUpdate({
73
- target: user.id,
74
- set: {
75
- name: user$1.name,
76
- email: user$1.email,
77
- emailVerified: user$1.emailVerified,
78
- permissions: [...user$1.permissions],
79
- updatedAt: user$1.updatedAt
80
- }
81
- });
82
- }
83
- });
84
-
85
- //#endregion
86
- //#region src/db/transaction.ts
87
- /**
88
- * Run a function within a database transaction
89
- * Creates a child scope with transaction-bound repositories
90
- *
91
- * Usage:
92
- * ```typescript
93
- * const result = await runInTransaction(container, async (txScope) => {
94
- * const { userRepository } = txScope.cradle
95
- * await userRepository.save(user)
96
- * return user
97
- * })
98
- * ```
99
- */
100
- const runInTransaction = async (container, fn) => {
101
- return container.resolve("db").transaction(async (tx) => {
102
- const txScope = container.createScope();
103
- txScope.register({
104
- db: asValue(tx),
105
- userRepository: asFunction(({ db }) => makeDrizzleUserRepository(db)).scoped()
106
- });
107
- try {
108
- return await fn(txScope);
109
- } finally {
110
- txScope.dispose();
111
- }
112
- });
113
- };
114
-
115
- //#endregion
116
- //#region src/logging/structured-logger.ts
117
- /**
118
- * Structured logger implementation
119
- * - JSON logs compatible with Loki
120
- * - File + stdout support
121
- * - Prometheus metrics collection
122
- */
123
- var StructuredLogger = class {
124
- fileStream = null;
125
- metrics;
126
- constructor(options = {}) {
127
- this.options = options;
128
- this.metrics = new MetricsCollector();
129
- if (options.enableFile && options.logDir) this.initFileStream(options.logDir);
130
- }
131
- initFileStream(logDir) {
132
- if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });
133
- this.fileStream = createWriteStream(path.join(logDir, `app-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.log`), { flags: "a" });
134
- }
135
- debug(message, meta) {
136
- this.log("DEBUG", message, meta);
137
- }
138
- info(message, meta) {
139
- this.log("INFO", message, meta);
140
- }
141
- warn(message, meta) {
142
- this.log("WARN", message, meta);
143
- }
144
- error(message, meta) {
145
- const context = meta instanceof Error ? {
146
- error: meta.message,
147
- stack: meta.stack
148
- } : meta;
149
- this.log("ERROR", message, context);
150
- }
151
- log(level, message, meta) {
152
- if (this.shouldSkip(level)) return;
153
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
154
- const logEntry = this.formatLog(level, message, meta, timestamp);
155
- console.log(logEntry);
156
- if (this.fileStream) this.fileStream.write(logEntry + "\n");
157
- this.metrics.recordLog(level, meta?.feature);
158
- }
159
- shouldSkip(level) {
160
- const levels = [
161
- "DEBUG",
162
- "INFO",
163
- "WARN",
164
- "ERROR"
165
- ];
166
- const minLevel = this.options.minLevel || "DEBUG";
167
- return levels.indexOf(level) < levels.indexOf(minLevel);
168
- }
169
- formatLog(level, message, meta, timestamp) {
170
- const logObject = {
171
- timestamp,
172
- level,
173
- message,
174
- ...meta
175
- };
176
- if (meta?.requestId) logObject.requestId = meta.requestId;
177
- if (meta?.userId) logObject.userId = meta.userId;
178
- if (meta?.feature) logObject.feature = meta.feature;
179
- return JSON.stringify(logObject);
180
- }
181
- /**
182
- * Get metrics in Prometheus text format
183
- */
184
- getMetrics() {
185
- return this.metrics.toPrometheusFormat();
186
- }
187
- /**
188
- * Cleanup resources
189
- */
190
- async dispose() {
191
- return new Promise((resolve) => {
192
- if (this.fileStream) this.fileStream.end(() => resolve());
193
- else resolve();
194
- });
195
- }
196
- };
197
- /**
198
- * Prometheus metrics collector
199
- */
200
- var MetricsCollector = class {
201
- logCount = new Map([
202
- ["DEBUG", 0],
203
- ["INFO", 0],
204
- ["WARN", 0],
205
- ["ERROR", 0]
206
- ]);
207
- featureCount = /* @__PURE__ */ new Map();
208
- recordLog(level, feature) {
209
- this.logCount.set(level, (this.logCount.get(level) || 0) + 1);
210
- if (feature) this.featureCount.set(feature, (this.featureCount.get(feature) || 0) + 1);
211
- }
212
- toPrometheusFormat() {
213
- const lines = ["# HELP app_logs_total Total number of logs by level", "# TYPE app_logs_total counter"];
214
- for (const [level, count] of this.logCount) lines.push(`app_logs_total{level="${level}"} ${count}`);
215
- lines.push("");
216
- lines.push("# HELP app_feature_logs_total Total logs by feature");
217
- lines.push("# TYPE app_feature_logs_total counter");
218
- for (const [feature, count] of this.featureCount) lines.push(`app_feature_logs_total{feature="${feature}"} ${count}`);
219
- return lines.join("\n");
220
- }
221
- };
222
- /**
223
- * Create logger instance
224
- */
225
- const makeStructuredLogger = (options) => {
226
- return new StructuredLogger(options);
227
- };
228
-
229
- //#endregion
230
- //#region src/logging/request-logger.ts
231
- /**
232
- * Create a request-scoped logger that automatically includes
233
- * requestId and userId in all log entries.
234
- *
235
- * This wraps the base singleton logger and enriches all log
236
- * calls with request context for Loki-compatible structured logging.
237
- */
238
- const makeRequestLogger = (options) => {
239
- const context = {
240
- requestId: options.requestId,
241
- ...options.userId && { userId: options.userId }
242
- };
243
- return {
244
- debug: (message, meta) => {
245
- options.baseLogger.debug(message, {
246
- ...context,
247
- ...meta
248
- });
249
- },
250
- info: (message, meta) => {
251
- options.baseLogger.info(message, {
252
- ...context,
253
- ...meta
254
- });
255
- },
256
- warn: (message, meta) => {
257
- options.baseLogger.warn(message, {
258
- ...context,
259
- ...meta
260
- });
261
- },
262
- error: (message, meta) => {
263
- if (meta instanceof Error) options.baseLogger.error(message, {
264
- ...context,
265
- error: meta.message,
266
- stack: meta.stack
267
- });
268
- else options.baseLogger.error(message, {
269
- ...context,
270
- ...meta
271
- });
272
- }
273
- };
274
- };
275
-
276
- //#endregion
277
- //#region ../domain/src/errors/index.ts
278
- /**
279
- * Severity levels for error classification
280
- */
281
- let ErrorSeverity = /* @__PURE__ */ function(ErrorSeverity$1) {
282
- ErrorSeverity$1["LOW"] = "LOW";
283
- ErrorSeverity$1["MEDIUM"] = "MEDIUM";
284
- ErrorSeverity$1["HIGH"] = "HIGH";
285
- ErrorSeverity$1["CRITICAL"] = "CRITICAL";
286
- return ErrorSeverity$1;
287
- }({});
288
- /**
289
- * Application error base class
290
- */
291
- var AppError$1 = class AppError$1 extends Error {
292
- code;
293
- severity;
294
- statusCode;
295
- meta;
296
- constructor(code, message, options = {}) {
297
- super(message);
298
- this.name = "AppError";
299
- this.code = code;
300
- this.severity = options.severity || ErrorSeverity.MEDIUM;
301
- this.statusCode = options.statusCode || 500;
302
- this.meta = options.meta || {};
303
- Object.setPrototypeOf(this, AppError$1.prototype);
304
- }
305
- toJSON() {
306
- return {
307
- name: this.name,
308
- code: this.code,
309
- message: this.message,
310
- severity: this.severity,
311
- statusCode: this.statusCode,
312
- meta: this.meta
313
- };
314
- }
315
- };
316
-
317
- //#endregion
318
- //#region src/errors/error-handler.ts
319
- /**
320
- * Error handler for structured error logging and serialization
321
- */
322
- var ErrorHandler = class ErrorHandler {
323
- constructor(logger) {
324
- this.logger = logger;
325
- }
326
- /**
327
- * Handle error and serialize for API response
328
- */
329
- handle(error, context) {
330
- const appError = this.normalize(error);
331
- this.log(appError, context);
332
- return this.serialize(appError);
333
- }
334
- /**
335
- * Normalize any error to AppError
336
- */
337
- normalize(error) {
338
- if (error instanceof AppError$1) return error;
339
- if (error instanceof Error) return new AppError$1("INTERNAL_SERVER_ERROR", error.message, {
340
- statusCode: 500,
341
- meta: { originalError: error.name }
342
- });
343
- return new AppError$1("INTERNAL_SERVER_ERROR", "Unknown error occurred", { statusCode: 500 });
344
- }
345
- /**
346
- * Log error with appropriate level
347
- */
348
- log(error, context) {
349
- const logContext = {
350
- code: error.code,
351
- severity: error.severity,
352
- stack: error.stack,
353
- ...context
354
- };
355
- switch (error.severity) {
356
- case "LOW":
357
- this.logger.debug(error.message, logContext);
358
- break;
359
- case "MEDIUM":
360
- this.logger.warn(error.message, logContext);
361
- break;
362
- case "HIGH":
363
- case "CRITICAL":
364
- this.logger.error(error.message, logContext);
365
- break;
366
- }
367
- }
368
- /**
369
- * Serialize error for API response
370
- */
371
- serialize(error) {
372
- return {
373
- code: error.code,
374
- message: error.message,
375
- statusCode: error.statusCode,
376
- ...Object.keys(error.meta).length > 0 && { meta: error.meta }
377
- };
378
- }
379
- /**
380
- * Create error handler factory
381
- */
382
- static create(logger) {
383
- return new ErrorHandler(logger);
384
- }
385
- };
386
- /**
387
- * Factory for error handler
388
- */
389
- const makeErrorHandler = (logger) => {
390
- return ErrorHandler.create(logger);
391
- };
392
-
393
- //#endregion
394
- //#region src/events/in-memory-event-bus.ts
395
- /**
396
- * In-memory event bus
397
- * Stores handlers in Sets, executes synchronously or asynchronously
398
- */
399
- var InMemoryEventBus = class {
400
- handlers = /* @__PURE__ */ new Map();
401
- allHandlers = /* @__PURE__ */ new Set();
402
- constructor(logger) {
403
- this.logger = logger;
404
- }
405
- async publish(event) {
406
- const eventName = String(event.name);
407
- const handlers = this.handlers.get(eventName) ?? /* @__PURE__ */ new Set();
408
- const allHandlers = Array.from(this.allHandlers);
409
- const eventHandlers = [...handlers, ...allHandlers];
410
- const failed = (await Promise.allSettled(eventHandlers.map((handler) => Promise.resolve(handler(event)).catch((error) => {
411
- this.logger.error("Error in event handler", {
412
- eventName,
413
- eventId: event.meta.eventId,
414
- error
415
- });
416
- })))).filter((r) => r.status === "rejected").length;
417
- if (failed > 0) this.logger.warn(`Event published with ${failed} handler errors`, {
418
- eventName,
419
- eventId: event.meta.eventId
420
- });
421
- }
422
- publishAsync(event) {
423
- queueMicrotask(() => {
424
- this.publish(event).catch((error) => {
425
- this.logger.error("Unhandled error in async event publishing", {
426
- eventName: event.name,
427
- eventId: event.meta.eventId,
428
- error
429
- });
430
- });
431
- });
432
- }
433
- subscribe(eventName, handler) {
434
- const eventNameStr = String(eventName);
435
- if (!this.handlers.has(eventNameStr)) this.handlers.set(eventNameStr, /* @__PURE__ */ new Set());
436
- const handlerSet = this.handlers.get(eventNameStr);
437
- handlerSet.add(handler);
438
- return () => {
439
- handlerSet.delete(handler);
440
- if (handlerSet.size === 0) this.handlers.delete(eventNameStr);
441
- };
442
- }
443
- subscribeAll(handler) {
444
- this.allHandlers.add(handler);
445
- return () => {
446
- this.allHandlers.delete(handler);
447
- };
448
- }
449
- };
450
-
451
- //#endregion
452
- //#region src/events/event-publisher-adapter.ts
453
- /**
454
- * Adapter that implements EventPublisher port using EventBus
455
- * Converts the simplified EventPublisher interface to full EventBus types
456
- */
457
- const makeEventPublisherAdapter = (eventBus, logger) => {
458
- return {
459
- async publish(eventName, payload, aggregateId) {
460
- const event = {
461
- name: eventName,
462
- payload,
463
- meta: {
464
- eventId: randomUUID(),
465
- occurredAt: /* @__PURE__ */ new Date(),
466
- version: 1,
467
- aggregateId,
468
- correlationId: randomUUID()
469
- }
470
- };
471
- logger.debug("Publishing event", {
472
- eventName,
473
- eventId: event.meta.eventId,
474
- aggregateId
475
- });
476
- await eventBus.publish(event);
477
- },
478
- subscribe(eventName, handler) {
479
- return eventBus.subscribe(eventName, async (event) => {
480
- try {
481
- await handler(event.payload);
482
- } catch (error) {
483
- logger.error("Error in event handler", {
484
- eventName,
485
- error: error instanceof Error ? error.message : "Unknown error"
486
- });
487
- }
488
- });
489
- }
490
- };
491
- };
492
-
493
- //#endregion
494
- //#region src/events/in-memory-event-publisher.ts
495
- /**
496
- * In-memory event publisher implementation
497
- * Suitable for development and testing
498
- */
499
- const makeInMemoryEventPublisher = () => {
500
- const subscribers = /* @__PURE__ */ new Map();
501
- return {
502
- async publish(eventName, payload, _aggregateId) {
503
- const handlers = subscribers.get(eventName);
504
- if (!handlers) return;
505
- await Promise.all(Array.from(handlers).map((handler) => Promise.resolve(handler(payload))));
506
- },
507
- subscribe(eventName, handler) {
508
- if (!subscribers.has(eventName)) subscribers.set(eventName, /* @__PURE__ */ new Set());
509
- const handlers = subscribers.get(eventName);
510
- handlers.add(handler);
511
- return () => {
512
- handlers.delete(handler);
513
- if (handlers.size === 0) subscribers.delete(eventName);
514
- };
515
- }
516
- };
517
- };
518
-
519
- //#endregion
520
- //#region src/cache/in-memory-cache-store.ts
521
- /**
522
- * In-memory cache store implementation
523
- * Suitable for single-process applications or development
524
- * For distributed systems, use Redis adapter
525
- */
526
- var InMemoryCacheStore = class {
527
- cache = /* @__PURE__ */ new Map();
528
- cleanupTimer = null;
529
- constructor(cleanupIntervalMs = 6e4) {
530
- this.cleanupIntervalMs = cleanupIntervalMs;
531
- this.startCleanup();
532
- }
533
- async get(key) {
534
- const entry = this.cache.get(key);
535
- if (!entry) return;
536
- if (entry.expiresAt && entry.expiresAt < Date.now()) {
537
- this.cache.delete(key);
538
- return;
539
- }
540
- return entry.value;
541
- }
542
- async set(key, value, ttlMs) {
543
- const entry = {
544
- value,
545
- expiresAt: ttlMs ? Date.now() + ttlMs : void 0
546
- };
547
- this.cache.set(key, entry);
548
- }
549
- async del(key) {
550
- this.cache.delete(key);
551
- }
552
- async clear() {
553
- this.cache.clear();
554
- }
555
- async has(key) {
556
- const entry = this.cache.get(key);
557
- if (!entry) return false;
558
- if (entry.expiresAt && entry.expiresAt < Date.now()) {
559
- this.cache.delete(key);
560
- return false;
561
- }
562
- return true;
563
- }
564
- /**
565
- * Start background cleanup of expired entries
566
- */
567
- startCleanup() {
568
- this.cleanupTimer = setInterval(() => {
569
- const now = Date.now();
570
- let expired = 0;
571
- for (const [key, entry] of this.cache.entries()) if (entry.expiresAt && entry.expiresAt < now) {
572
- this.cache.delete(key);
573
- expired++;
574
- }
575
- if (expired > 0) {}
576
- }, this.cleanupIntervalMs);
577
- }
578
- /**
579
- * Stop cleanup timer
580
- */
581
- destroy() {
582
- if (this.cleanupTimer) {
583
- clearInterval(this.cleanupTimer);
584
- this.cleanupTimer = null;
585
- }
586
- }
587
- };
588
-
589
- //#endregion
590
- //#region src/rate-limiter/in-memory-rate-limiter.ts
591
- /**
592
- * In-memory rate limiter using token bucket algorithm
593
- * Suitable for single-process applications or development
594
- * For distributed systems, use Redis adapter
595
- */
596
- var InMemoryRateLimiterStore = class {
597
- buckets = /* @__PURE__ */ new Map();
598
- cleanupTimer = null;
599
- constructor(cleanupIntervalMs = 6e4) {
600
- this.cleanupIntervalMs = cleanupIntervalMs;
601
- this.startCleanup();
602
- }
603
- async checkLimit(key, capacity, refillPerSecond) {
604
- const now = Date.now();
605
- let bucket = this.buckets.get(key);
606
- if (!bucket) {
607
- bucket = {
608
- tokens: capacity,
609
- lastRefillAt: now
610
- };
611
- this.buckets.set(key, bucket);
612
- }
613
- const tokensToAdd = (now - bucket.lastRefillAt) / 1e3 * refillPerSecond;
614
- bucket.tokens = Math.min(capacity, bucket.tokens + tokensToAdd);
615
- bucket.lastRefillAt = now;
616
- const allowed = bucket.tokens >= 1;
617
- if (allowed) bucket.tokens -= 1;
618
- const secondsToReset = Math.max(0, 1 - bucket.tokens) / refillPerSecond;
619
- const resetAt = new Date(now + secondsToReset * 1e3);
620
- return {
621
- allowed,
622
- tokensRemaining: Math.floor(bucket.tokens),
623
- resetAt
624
- };
625
- }
626
- async reset(key) {
627
- this.buckets.delete(key);
628
- }
629
- async clear() {
630
- this.buckets.clear();
631
- }
632
- /**
633
- * Start background cleanup of old buckets
634
- * Removes buckets that haven't been used in a long time
635
- */
636
- startCleanup() {
637
- this.cleanupTimer = setInterval(() => {
638
- const now = Date.now();
639
- const maxAgeMs = 36e5;
640
- let removed = 0;
641
- for (const [key, bucket] of this.buckets.entries()) if (now - bucket.lastRefillAt > maxAgeMs) {
642
- this.buckets.delete(key);
643
- removed++;
644
- }
645
- if (removed > 0) {}
646
- }, this.cleanupIntervalMs);
647
- }
648
- /**
649
- * Stop cleanup timer
650
- */
651
- destroy() {
652
- if (this.cleanupTimer) {
653
- clearInterval(this.cleanupTimer);
654
- this.cleanupTimer = null;
655
- }
656
- }
657
- };
658
-
659
- //#endregion
660
- //#region src/decorators/use-case-decorator.ts
661
- /**
662
- * Base decorator that adds performance monitoring
663
- */
664
- const withPerformanceMonitoring = (useCase, context, featureName) => {
665
- return (async (...args) => {
666
- const startTime = Date.now();
667
- try {
668
- const result = await useCase(...args);
669
- const durationMs = Date.now() - startTime;
670
- context.logger.debug(`${featureName}_completed`, {
671
- requestId: context.requestId,
672
- feature: featureName,
673
- durationMs
674
- });
675
- return result;
676
- } catch (error) {
677
- const durationMs = Date.now() - startTime;
678
- if (error instanceof AppError$1) context.logger.debug(`${featureName}_error`, {
679
- requestId: context.requestId,
680
- feature: featureName,
681
- durationMs,
682
- code: error.code,
683
- severity: error.severity
684
- });
685
- throw error;
686
- }
687
- });
688
- };
689
- /**
690
- * Base decorator that adds error handling and conversion
691
- */
692
- const withErrorHandling = (useCase, context, featureName) => {
693
- return (async (...args) => {
694
- try {
695
- return await useCase(...args);
696
- } catch (error) {
697
- if (error instanceof AppError$1) throw error;
698
- if (error instanceof Error) {
699
- context.logger.error(`${featureName}_unexpected_error`, {
700
- requestId: context.requestId,
701
- feature: featureName,
702
- errorName: error.name,
703
- errorMessage: error.message,
704
- stack: error.stack
705
- });
706
- throw new AppError$1("INTERNAL_SERVER_ERROR", `Unexpected error in ${featureName}`, {
707
- statusCode: 500,
708
- meta: {
709
- originalError: error.message,
710
- feature: featureName
711
- }
712
- });
713
- }
714
- context.logger.error(`${featureName}_unknown_error`, {
715
- requestId: context.requestId,
716
- feature: featureName
717
- });
718
- throw new AppError$1("INTERNAL_SERVER_ERROR", `Unknown error in ${featureName}`, {
719
- statusCode: 500,
720
- meta: { feature: featureName }
721
- });
722
- }
723
- });
724
- };
725
- /**
726
- * Request tracing decorator - adds context to logs
727
- */
728
- const withRequestTracing = (useCase, context, featureName) => {
729
- return (async (...args) => {
730
- context.logger.info(`${featureName}_started`, {
731
- requestId: context.requestId,
732
- userId: context.userId,
733
- feature: featureName
734
- });
735
- try {
736
- const result = await useCase(...args);
737
- context.logger.info(`${featureName}_success`, {
738
- requestId: context.requestId,
739
- userId: context.userId,
740
- feature: featureName
741
- });
742
- return result;
743
- } catch (error) {
744
- context.logger.error(`${featureName}_failed`, {
745
- requestId: context.requestId,
746
- userId: context.userId,
747
- feature: featureName,
748
- error: error instanceof Error ? error.message : "Unknown error"
749
- });
750
- throw error;
751
- }
752
- });
753
- };
754
- /**
755
- * Compose multiple decorators together
756
- * Order: Tracing → Performance → Error Handling
757
- * (outermost → innermost)
758
- */
759
- const decorateUseCase = (useCase, context, featureName) => {
760
- let decorated = withErrorHandling(useCase, context, featureName);
761
- decorated = withPerformanceMonitoring(decorated, context, featureName);
762
- decorated = withRequestTracing(decorated, context, featureName);
763
- return decorated;
764
- };
765
- /**
766
- * Factory for creating decorated use cases
767
- * Used in dependency injection to wrap use cases automatically
768
- */
769
- const makeDecoratedUseCase = (useCase, context, featureName) => {
770
- return decorateUseCase(useCase, context, featureName);
771
- };
772
-
773
- //#endregion
774
- //#region src/decorators/apply-decorators.ts
775
- /**
776
- * Helper to apply decorators to use cases in module registration
777
- * Reduces boilerplate in feature modules
778
- *
779
- * Usage:
780
- * ```
781
- * getUserProfile: asFunction(({ userRepository, ...deps }) =>
782
- * applyDecorators(
783
- * makeGetUserProfile({ userRepository }),
784
- * deps,
785
- * 'getUserProfile'
786
- * )
787
- * ).scoped(),
788
- * ```
789
- */
790
- const applyDecorators = (useCase, deps, featureName) => {
791
- return decorateUseCase(useCase, {
792
- logger: deps.logger,
793
- requestId: deps.requestId,
794
- feature: featureName
795
- }, featureName);
796
- };
797
-
798
- //#endregion
799
- //#region src/decorators/with-caching.ts
800
- /**
801
- * Wraps a use case with caching
802
- * - Only works with idempotent read operations
803
- * - Key function determines cache key from input
804
- * - TTL controls cache expiration
805
- * - SWR allows returning stale value while revalidating
806
- *
807
- * @example
808
- * ```ts
809
- * const cachedGetUser = withCaching(
810
- * getUserProfile,
811
- * cacheStore,
812
- * {
813
- * keyFn: (input) => `user:${input.userId}`,
814
- * ttlMs: 60000, // 1 minute
815
- * staleWhileRevalidateMs: 120000, // 2 minutes
816
- * }
817
- * )
818
- * ```
819
- */
820
- const withCaching = (fn, cacheStore, options) => {
821
- const cached = async (input) => {
822
- const cacheKey = options.keyFn(input);
823
- const cached$1 = await cacheStore.get(cacheKey);
824
- if (cached$1 !== void 0) return cached$1;
825
- const result = await fn(input);
826
- await cacheStore.set(cacheKey, result, options.ttlMs);
827
- return result;
828
- };
829
- Object.defineProperty(cached, "name", { value: `withCaching(${fn.name || "anonymous"})` });
830
- return cached;
831
- };
832
-
833
- //#endregion
834
- //#region src/decorators/with-retry.ts
835
- /**
836
- * Wraps a use case with retry logic
837
- * - Uses exponential backoff
838
- * - Only retries on specific errors via retryOn predicate
839
- * - Backoff is capped at maxBackoffMs
840
- *
841
- * @example
842
- * ```ts
843
- * const retriedGetUser = withRetry(
844
- * getUserProfile,
845
- * {
846
- * maxAttempts: 3,
847
- * initialBackoffMs: 100,
848
- * maxBackoffMs: 30000,
849
- * backoffMultiplier: 2,
850
- * retryOn: (error) => error.message.includes('TIMEOUT'),
851
- * }
852
- * )
853
- * ```
854
- */
855
- const withRetry = (fn, options = {}) => {
856
- const maxAttempts = options.maxAttempts ?? 3;
857
- const initialBackoffMs = options.initialBackoffMs ?? 100;
858
- const maxBackoffMs = options.maxBackoffMs ?? 3e4;
859
- const backoffMultiplier = options.backoffMultiplier ?? 2;
860
- const retryOn = options.retryOn ?? ((error) => error instanceof Error);
861
- const retried = async (...args) => {
862
- let lastError = null;
863
- let backoffMs = initialBackoffMs;
864
- for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
865
- return await fn(...args);
866
- } catch (error) {
867
- lastError = error instanceof Error ? error : new Error(String(error));
868
- if (!retryOn(lastError)) throw error;
869
- if (attempt < maxAttempts) {
870
- await new Promise((resolve) => setTimeout(resolve, backoffMs));
871
- backoffMs = Math.min(backoffMs * backoffMultiplier, maxBackoffMs);
872
- }
873
- }
874
- throw lastError ?? /* @__PURE__ */ new Error("Retry failed without error");
875
- };
876
- Object.defineProperty(retried, "name", { value: `withRetry(${fn.name || "anonymous"})` });
877
- return retried;
878
- };
879
-
880
- //#endregion
881
- //#region src/decorators/with-rate-limit.ts
882
- /**
883
- * Rate limit error
884
- */
885
- var RateLimitError = class extends AppError {
886
- constructor(resetAt) {
887
- super(ErrorCode.RATE_LIMITED, `Rate limit exceeded. Reset at ${resetAt.toISOString()}`, {
888
- statusCode: 429,
889
- meta: { resetAt }
890
- });
891
- this.resetAt = resetAt;
892
- }
893
- };
894
- /**
895
- * Wraps a use case with rate limiting
896
- * - Uses token bucket algorithm
897
- * - Per-input rate limit tracking via keyFn
898
- * - Throws RateLimitError when limit exceeded
899
- *
900
- * @example
901
- * ```ts
902
- * const limitedCreatePost = withRateLimit(
903
- * createPost,
904
- * rateLimiterStore,
905
- * {
906
- * keyFn: (input) => `user:${input.userId}`,
907
- * capacity: 10,
908
- * refillPerSecond: 1, // 1 request per second
909
- * }
910
- * )
911
- * ```
912
- */
913
- const withRateLimit = (fn, rateLimiterStore, options) => {
914
- const limited = async (input) => {
915
- const limitKey = options.keyFn(input);
916
- const result = await rateLimiterStore.checkLimit(limitKey, options.capacity, options.refillPerSecond);
917
- if (!result.allowed) throw new RateLimitError(result.resetAt);
918
- return await fn(input);
919
- };
920
- Object.defineProperty(limited, "name", { value: `withRateLimit(${fn.name || "anonymous"})` });
921
- return limited;
922
- };
923
-
924
- //#endregion
925
- //#region src/modules/core.module.ts
926
- /**
927
- * Core module: singleton registrations for infrastructure
928
- * - Database pool and connection
929
- * - Clock
930
- * - Logger (structured, Loki/Prometheus compatible)
931
- * - AI provider
932
- * - Auth
933
- */
934
- const registerCoreModule = (container) => {
935
- container.register({
936
- dbPool: asFunction(({ config }) => createDbPool(config.databaseUrl)).singleton(),
937
- db: asFunction(({ dbPool }) => createDb(dbPool)).singleton(),
938
- clock: asValue(new SystemClock()),
939
- logger: asFunction(({ config }) => makeStructuredLogger({
940
- enableFile: config.enableFileLogging,
941
- logDir: config.logDir,
942
- minLevel: config.logLevel
943
- })).singleton(),
944
- errorHandler: asFunction(({ logger }) => makeErrorHandler(logger)).singleton(),
945
- auth: asValue(auth),
946
- aiProvider: asFunction(() => google("gemini-2.5-flash")).singleton(),
947
- eventBus: asFunction(({ logger }) => new InMemoryEventBus(logger)).singleton(),
948
- cacheStore: asClass(InMemoryCacheStore).singleton(),
949
- rateLimiterStore: asClass(InMemoryRateLimiterStore).singleton(),
950
- requestLogger: asFunction(({ logger, requestId, session }) => makeRequestLogger({
951
- requestId,
952
- userId: session?.user?.id,
953
- baseLogger: logger
954
- })).scoped()
955
- });
956
- };
957
-
958
- //#endregion
959
- //#region src/modules/user.module.ts
960
- /**
961
- * User repository module: registers user-related repositories
962
- *
963
- * Note: Use case registrations are in apps/server/src/modules/user.module.ts
964
- * to maintain Clean Architecture layering (Infrastructure should not import Application)
965
- */
966
- const registerUserModule = (container) => {
967
- container.register({ userRepository: asFunction(({ db }) => makeDrizzleUserRepository(db)).scoped() });
968
- };
969
-
970
- //#endregion
971
29
  export { ErrorHandler, InMemoryCacheStore, InMemoryEventBus, InMemoryRateLimiterStore, RateLimitError, StructuredLogger, applyDecorators, createDb, createDbPool, decorateUseCase, makeDecoratedUseCase, makeDrizzleUserRepository, makeErrorHandler, makeEventPublisherAdapter, makeInMemoryEventPublisher, makeRequestLogger, makeStructuredLogger, registerCoreModule, registerUserModule, runInTransaction, withCaching, withErrorHandling, withPerformanceMonitoring, withRateLimit, withRequestTracing, withRetry };