@plyaz/core 1.2.1 → 1.3.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 (37) hide show
  1. package/README.md +248 -183
  2. package/dist/domain/example/FrontendExampleDomainService.d.ts.map +1 -1
  3. package/dist/domain/featureFlags/providers/database.d.ts +6 -1
  4. package/dist/domain/featureFlags/providers/database.d.ts.map +1 -1
  5. package/dist/entry-backend.d.ts +3 -1
  6. package/dist/entry-backend.d.ts.map +1 -1
  7. package/dist/entry-backend.js +3145 -3325
  8. package/dist/entry-backend.js.map +1 -1
  9. package/dist/entry-backend.mjs +2732 -2899
  10. package/dist/entry-backend.mjs.map +1 -1
  11. package/dist/entry-frontend.js +1594 -1406
  12. package/dist/entry-frontend.js.map +1 -1
  13. package/dist/entry-frontend.mjs +1568 -1379
  14. package/dist/entry-frontend.mjs.map +1 -1
  15. package/dist/index.js +3130 -3318
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.mjs +3133 -3308
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/init/CoreInitializer.d.ts +9 -8
  20. package/dist/init/CoreInitializer.d.ts.map +1 -1
  21. package/dist/init/ServiceRegistry.d.ts.map +1 -1
  22. package/dist/init/nestjs/index.js +1511 -1336
  23. package/dist/init/nestjs/index.js.map +1 -1
  24. package/dist/init/nestjs/index.mjs +1527 -1352
  25. package/dist/init/nestjs/index.mjs.map +1 -1
  26. package/dist/models/example/ExampleRepository.d.ts +45 -3
  27. package/dist/models/example/ExampleRepository.d.ts.map +1 -1
  28. package/dist/models/featureFlags/FeatureFlagRepository.d.ts +72 -471
  29. package/dist/models/featureFlags/FeatureFlagRepository.d.ts.map +1 -1
  30. package/dist/services/DbService.d.ts +2 -0
  31. package/dist/services/DbService.d.ts.map +1 -1
  32. package/dist/services/NotificationService.d.ts +2 -0
  33. package/dist/services/NotificationService.d.ts.map +1 -1
  34. package/dist/services/StorageService.d.ts +2 -0
  35. package/dist/services/StorageService.d.ts.map +1 -1
  36. package/dist/utils/common/id.d.ts.map +1 -1
  37. package/package.json +3 -3
@@ -1,19 +1,19 @@
1
1
  'use strict';
2
2
 
3
- var logger$1 = require('@plyaz/logger');
3
+ var config = require('@plyaz/config');
4
+ var types = require('@plyaz/types');
5
+ var events = require('events');
4
6
  var db = require('@plyaz/db');
5
7
  var errors = require('@plyaz/errors');
6
8
  var errors$1 = require('@plyaz/types/errors');
7
9
  var core = require('@plyaz/types/core');
8
- var events = require('events');
9
- var config = require('@plyaz/config');
10
- var types = require('@plyaz/types');
10
+ var storage = require('@plyaz/storage');
11
+ var notifications = require('@plyaz/notifications');
12
+ var logger$1 = require('@plyaz/logger');
11
13
  var api = require('@plyaz/api');
12
14
  var common = require('@nestjs/common');
13
15
  var rxjs = require('rxjs');
14
16
  var features = require('@plyaz/types/features');
15
- var storage = require('@plyaz/storage');
16
- var notifications = require('@plyaz/notifications');
17
17
  var store = require('@plyaz/store');
18
18
  var observability = require('@plyaz/types/observability');
19
19
  var examples = require('@plyaz/types/examples');
@@ -22,7 +22,15 @@ var middleware = require('@plyaz/errors/middleware');
22
22
  // @plyaz package - Built with tsup
23
23
  var __defProp = Object.defineProperty;
24
24
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
25
+ var __getOwnPropNames = Object.getOwnPropertyNames;
25
26
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
27
+ var __esm = (fn, res) => function __init() {
28
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
29
+ };
30
+ var __export = (target, all) => {
31
+ for (var name in all)
32
+ __defProp(target, name, { get: all[name], enumerable: true });
33
+ };
26
34
  var __decorateClass = (decorators, target, key, kind) => {
27
35
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
28
36
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
@@ -30,15 +38,12 @@ var __decorateClass = (decorators, target, key, kind) => {
30
38
  result = (decorator(result)) || result;
31
39
  return result;
32
40
  };
41
+ var init_hash = __esm({
42
+ "src/utils/common/hash.ts"() {
43
+ }
44
+ });
33
45
 
34
46
  // src/utils/common/id.ts
35
- var RANDOM_ID_RADIX = 36;
36
- var RANDOM_ID_SLICE_START = 2;
37
- var SHORT_ID_LENGTH = 8;
38
- var HEX_RADIX = 16;
39
- var HEX_SLICE_START = 2;
40
- var HEX_SLICE_END = 18;
41
- var SPAN_ID_LENGTH = 16;
42
47
  function generateId() {
43
48
  const cryptoApi = globalThis.crypto;
44
49
  if (cryptoApi?.randomUUID) {
@@ -46,7 +51,6 @@ function generateId() {
46
51
  }
47
52
  return `${Date.now()}-${Math.random().toString(RANDOM_ID_RADIX).slice(RANDOM_ID_SLICE_START)}`;
48
53
  }
49
- __name(generateId, "generateId");
50
54
  function generateShortId() {
51
55
  const cryptoApi = globalThis.crypto;
52
56
  if (cryptoApi?.randomUUID) {
@@ -54,11 +58,9 @@ function generateShortId() {
54
58
  }
55
59
  return Math.random().toString(RANDOM_ID_RADIX).slice(RANDOM_ID_SLICE_START, RANDOM_ID_SLICE_START + SHORT_ID_LENGTH);
56
60
  }
57
- __name(generateShortId, "generateShortId");
58
61
  function generateCorrelationId() {
59
62
  return `${Date.now()}-${generateShortId()}`;
60
63
  }
61
- __name(generateCorrelationId, "generateCorrelationId");
62
64
  function generateTraceId() {
63
65
  const cryptoApi = globalThis.crypto;
64
66
  if (cryptoApi?.randomUUID) {
@@ -66,7 +68,6 @@ function generateTraceId() {
66
68
  }
67
69
  return Math.random().toString(HEX_RADIX).substring(HEX_SLICE_START, HEX_SLICE_END) + Math.random().toString(HEX_RADIX).substring(HEX_SLICE_START, HEX_SLICE_END);
68
70
  }
69
- __name(generateTraceId, "generateTraceId");
70
71
  function generateSpanId() {
71
72
  const cryptoApi = globalThis.crypto;
72
73
  if (cryptoApi?.randomUUID) {
@@ -74,747 +75,1229 @@ function generateSpanId() {
74
75
  }
75
76
  return Math.random().toString(HEX_RADIX).substring(HEX_SLICE_START, HEX_SLICE_END);
76
77
  }
77
- __name(generateSpanId, "generateSpanId");
78
+ var RANDOM_ID_RADIX, RANDOM_ID_SLICE_START, SHORT_ID_LENGTH, HEX_RADIX, HEX_SLICE_START, HEX_SLICE_END, SPAN_ID_LENGTH;
79
+ var init_id = __esm({
80
+ "src/utils/common/id.ts"() {
81
+ RANDOM_ID_RADIX = 36;
82
+ RANDOM_ID_SLICE_START = 2;
83
+ SHORT_ID_LENGTH = 8;
84
+ HEX_RADIX = 16;
85
+ HEX_SLICE_START = 2;
86
+ HEX_SLICE_END = 18;
87
+ SPAN_ID_LENGTH = 16;
88
+ __name(generateId, "generateId");
89
+ __name(generateShortId, "generateShortId");
90
+ __name(generateCorrelationId, "generateCorrelationId");
91
+ __name(generateTraceId, "generateTraceId");
92
+ __name(generateSpanId, "generateSpanId");
93
+ }
94
+ });
95
+ var init_values = __esm({
96
+ "src/utils/common/values.ts"() {
97
+ }
98
+ });
78
99
 
79
- // src/events/CoreEventManager.ts
80
- var MAX_EVENT_LISTENERS = 100;
81
- var CoreEventManagerClass = class {
82
- constructor() {
83
- this.emitter = new events.EventEmitter();
84
- this.subscriptions = /* @__PURE__ */ new Map();
85
- this.emitter.setMaxListeners(MAX_EVENT_LISTENERS);
100
+ // src/utils/common/object.ts
101
+ var init_object = __esm({
102
+ "src/utils/common/object.ts"() {
86
103
  }
87
- static {
88
- __name(this, "CoreEventManagerClass");
89
- }
90
- emit(eventType, data) {
91
- const [scope] = eventType.split(":");
92
- const event = {
93
- type: eventType,
94
- scope: scope ?? "system",
95
- timestamp: Date.now(),
96
- correlationId: this.getCorrelationId(),
97
- data
98
- };
99
- this.emitter.emit(eventType, event);
100
- this.emitter.emit("*", event);
101
- return true;
102
- }
103
- on(eventType, handler) {
104
- this.emitter.on(eventType, handler);
105
- if (!this.subscriptions.has(eventType)) {
106
- this.subscriptions.set(eventType, /* @__PURE__ */ new Set());
107
- }
108
- this.subscriptions.get(eventType).add(handler);
109
- return () => {
110
- this.emitter.off(eventType, handler);
111
- this.subscriptions.get(eventType)?.delete(handler);
112
- };
113
- }
114
- once(eventType, handler) {
115
- const wrappedHandler = /* @__PURE__ */ __name((event) => {
116
- handler(event);
117
- this.emitter.off(eventType, wrappedHandler);
118
- this.subscriptions.get(eventType)?.delete(wrappedHandler);
119
- }, "wrappedHandler");
120
- this.emitter.on(eventType, wrappedHandler);
121
- if (!this.subscriptions.has(eventType)) {
122
- this.subscriptions.set(eventType, /* @__PURE__ */ new Set());
123
- }
124
- this.subscriptions.get(eventType).add(wrappedHandler);
125
- return () => {
126
- this.emitter.off(eventType, wrappedHandler);
127
- this.subscriptions.get(eventType)?.delete(wrappedHandler);
128
- };
104
+ });
105
+ var init_validation = __esm({
106
+ "src/utils/common/validation.ts"() {
129
107
  }
130
- /**
131
- * Subscribe to all events in a scope
132
- *
133
- * @param scope - Event scope to listen to (e.g., CoreEventScope.AUTH)
134
- * @param handler - Event handler
135
- * @returns Unsubscribe function
136
- *
137
- * @example
138
- * ```typescript
139
- * CoreEventManager.onScope(CoreEventScope.AUTH, (event) => {
140
- * // Handles auth:login, auth:logout, auth:tokenRefresh, etc.
141
- * console.log(`Auth event: ${event.type}`, event.data);
142
- * });
143
- * ```
144
- */
145
- onScope(scope, handler) {
146
- const wrappedHandler = /* @__PURE__ */ __name((event) => {
147
- if (event.scope === scope) {
148
- handler(event);
108
+ });
109
+
110
+ // src/utils/common/index.ts
111
+ var init_common = __esm({
112
+ "src/utils/common/index.ts"() {
113
+ init_hash();
114
+ init_id();
115
+ init_values();
116
+ init_object();
117
+ init_validation();
118
+ }
119
+ });
120
+ var MAX_EVENT_LISTENERS, CoreEventManagerClass, CoreEventManager;
121
+ var init_CoreEventManager = __esm({
122
+ "src/events/CoreEventManager.ts"() {
123
+ init_common();
124
+ MAX_EVENT_LISTENERS = 100;
125
+ CoreEventManagerClass = class {
126
+ constructor() {
127
+ this.emitter = new events.EventEmitter();
128
+ this.subscriptions = /* @__PURE__ */ new Map();
129
+ this.emitter.setMaxListeners(MAX_EVENT_LISTENERS);
149
130
  }
150
- }, "wrappedHandler");
151
- return this.on("*", wrappedHandler);
152
- }
153
- /**
154
- * Remove a specific handler from an event
155
- *
156
- * @param eventType - Event type
157
- * @param handler - Handler to remove
158
- */
159
- off(eventType, handler) {
160
- this.emitter.off(eventType, handler);
161
- this.subscriptions.get(eventType)?.delete(handler);
162
- }
163
- /**
164
- * Dispose all subscriptions
165
- */
166
- dispose() {
167
- this.subscriptions.forEach((handlers, eventType) => {
168
- handlers.forEach((handler) => this.emitter.off(eventType, handler));
169
- });
170
- this.subscriptions.clear();
171
- this.emitter.removeAllListeners();
172
- }
173
- /**
174
- * Generate correlation ID for event tracing
175
- */
176
- getCorrelationId() {
177
- return generateCorrelationId();
131
+ static {
132
+ __name(this, "CoreEventManagerClass");
133
+ }
134
+ emit(eventType, data) {
135
+ const [scope] = eventType.split(":");
136
+ const event = {
137
+ type: eventType,
138
+ scope: scope ?? "system",
139
+ timestamp: Date.now(),
140
+ correlationId: this.getCorrelationId(),
141
+ data
142
+ };
143
+ this.emitter.emit(eventType, event);
144
+ this.emitter.emit("*", event);
145
+ return true;
146
+ }
147
+ on(eventType, handler) {
148
+ this.emitter.on(eventType, handler);
149
+ if (!this.subscriptions.has(eventType)) {
150
+ this.subscriptions.set(eventType, /* @__PURE__ */ new Set());
151
+ }
152
+ this.subscriptions.get(eventType).add(handler);
153
+ return () => {
154
+ this.emitter.off(eventType, handler);
155
+ this.subscriptions.get(eventType)?.delete(handler);
156
+ };
157
+ }
158
+ once(eventType, handler) {
159
+ const wrappedHandler = /* @__PURE__ */ __name((event) => {
160
+ handler(event);
161
+ this.emitter.off(eventType, wrappedHandler);
162
+ this.subscriptions.get(eventType)?.delete(wrappedHandler);
163
+ }, "wrappedHandler");
164
+ this.emitter.on(eventType, wrappedHandler);
165
+ if (!this.subscriptions.has(eventType)) {
166
+ this.subscriptions.set(eventType, /* @__PURE__ */ new Set());
167
+ }
168
+ this.subscriptions.get(eventType).add(wrappedHandler);
169
+ return () => {
170
+ this.emitter.off(eventType, wrappedHandler);
171
+ this.subscriptions.get(eventType)?.delete(wrappedHandler);
172
+ };
173
+ }
174
+ /**
175
+ * Subscribe to all events in a scope
176
+ *
177
+ * @param scope - Event scope to listen to (e.g., CoreEventScope.AUTH)
178
+ * @param handler - Event handler
179
+ * @returns Unsubscribe function
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * CoreEventManager.onScope(CoreEventScope.AUTH, (event) => {
184
+ * // Handles auth:login, auth:logout, auth:tokenRefresh, etc.
185
+ * console.log(`Auth event: ${event.type}`, event.data);
186
+ * });
187
+ * ```
188
+ */
189
+ onScope(scope, handler) {
190
+ const wrappedHandler = /* @__PURE__ */ __name((event) => {
191
+ if (event.scope === scope) {
192
+ handler(event);
193
+ }
194
+ }, "wrappedHandler");
195
+ return this.on("*", wrappedHandler);
196
+ }
197
+ /**
198
+ * Remove a specific handler from an event
199
+ *
200
+ * @param eventType - Event type
201
+ * @param handler - Handler to remove
202
+ */
203
+ off(eventType, handler) {
204
+ this.emitter.off(eventType, handler);
205
+ this.subscriptions.get(eventType)?.delete(handler);
206
+ }
207
+ /**
208
+ * Dispose all subscriptions
209
+ */
210
+ dispose() {
211
+ this.subscriptions.forEach((handlers, eventType) => {
212
+ handlers.forEach((handler) => this.emitter.off(eventType, handler));
213
+ });
214
+ this.subscriptions.clear();
215
+ this.emitter.removeAllListeners();
216
+ }
217
+ /**
218
+ * Generate correlation ID for event tracing
219
+ */
220
+ getCorrelationId() {
221
+ return generateCorrelationId();
222
+ }
223
+ };
224
+ CoreEventManager = new CoreEventManagerClass();
178
225
  }
179
- };
180
- var CoreEventManager = new CoreEventManagerClass();
226
+ });
181
227
 
182
228
  // src/services/DbService.ts
183
- var DEFAULT_ENCRYPTION_FIELDS = {
184
- // User PII
185
- users: ["password_hash", "phone_number", "date_of_birth"],
186
- // KYC sensitive data
187
- "backoffice.kyc_submissions": ["tax_id", "address_line1", "address_line2"],
188
- // Connected accounts OAuth tokens
189
- connected_accounts: ["access_token_encrypted", "refresh_token_encrypted", "wallet_address"],
190
- // Payment payout accounts
191
- user_payout_accounts: ["provider_account_id", "account_holder_name"],
192
- // Sessions
193
- sessions: ["token"]
194
- };
195
- var DEFAULT_CACHE_TTL_SECONDS = 300;
196
- var DEFAULT_AUDIT_RETENTION_DAYS = 180;
197
- var DEFAULT_POOL_SIZE = 10;
198
- var DEFAULT_CONFIG = {
199
- adapter: "sql",
200
- cache: {
201
- enabled: true,
202
- provider: "memory",
203
- ttl: DEFAULT_CACHE_TTL_SECONDS
204
- },
205
- softDelete: {
206
- enabled: true,
207
- field: "deleted_at",
208
- excludeTables: [
209
- "audit_logs",
210
- "audit.audit_logs",
211
- "audit.feature_flag_evaluations",
212
- "feature_flag_evaluations",
213
- "notification_events",
214
- "payments_activity",
215
- "campaign_analytics",
216
- "admin_actions",
217
- "backoffice.admin_actions"
218
- ]
219
- },
220
- audit: {
221
- enabled: true,
222
- // Enabled by default for compliance
223
- retentionDays: DEFAULT_AUDIT_RETENTION_DAYS,
224
- excludeFields: ["password_hash", "api_key_hash", "token_hash"],
225
- excludeTables: ["audit_logs", "audit.audit_logs"],
226
- // Don't audit the audit table itself
227
- schema: "audit",
228
- // Use dedicated audit schema
229
- usePartitionedTables: true
230
- // Use daily partitioned tables (audit_log_yyyy_mm_dd)
231
- }
232
- // NOTE: Encryption requires key from environment or DbService.initialize()
233
- // Enable via: DbService.initialize({ encryption: { enabled: true, key: process.env.ENCRYPTION_KEY!, fields: DEFAULT_ENCRYPTION_FIELDS } })
234
- };
235
- var TABLE_REGISTRY = {
236
- // Migration 008: Feature Flags (custom ID column 'key')
237
- feature_flags: { idColumn: "key" },
238
- // Test Feature Flags (custom ID column 'key')
239
- test_feature_flags: { idColumn: "key" },
240
- // Migration 010: Universal Analytics (custom ID column 'user_id')
241
- user_analytics_summary: { idColumn: "user_id" }
242
- };
243
- var DbService = class _DbService {
244
- constructor() {
245
- this.databaseService = null;
246
- this.namedAdapters = /* @__PURE__ */ new Map();
247
- this.config = null;
248
- this.initialized = false;
249
- }
250
- static {
251
- __name(this, "DbService");
252
- }
253
- static {
254
- this.instance = null;
255
- }
256
- /**
257
- * Emits a database error event via CoreEventManager.
258
- * Called when database operations fail to integrate with global error handling.
259
- *
260
- * @param error - The error that occurred
261
- * @param operation - The operation that failed (e.g., 'transaction', 'query', 'healthCheck')
262
- * @param table - Optional table name involved in the operation
263
- * @param query - Optional query string that failed
264
- * @param recoverable - Whether the error is recoverable (default: false)
265
- */
266
- emitDatabaseError(error, operation, options) {
267
- const payload = {
268
- error,
269
- operation,
270
- table: options?.table,
271
- query: options?.query,
272
- recoverable: options?.recoverable ?? false
229
+ var DbService_exports = {};
230
+ __export(DbService_exports, {
231
+ DEFAULT_ENCRYPTION_FIELDS: () => DEFAULT_ENCRYPTION_FIELDS,
232
+ DbService: () => DbService,
233
+ TABLE_REGISTRY: () => TABLE_REGISTRY
234
+ });
235
+ var DEFAULT_ENCRYPTION_FIELDS, DEFAULT_CACHE_TTL_SECONDS, DEFAULT_AUDIT_RETENTION_DAYS, DEFAULT_POOL_SIZE, DEFAULT_CONFIG, TABLE_REGISTRY, DbService;
236
+ var init_DbService = __esm({
237
+ "src/services/DbService.ts"() {
238
+ init_CoreEventManager();
239
+ DEFAULT_ENCRYPTION_FIELDS = {
240
+ // User PII
241
+ users: ["password_hash", "phone_number", "date_of_birth"],
242
+ // KYC sensitive data
243
+ "backoffice.kyc_submissions": ["tax_id", "address_line1", "address_line2"],
244
+ // Connected accounts OAuth tokens
245
+ connected_accounts: ["access_token_encrypted", "refresh_token_encrypted", "wallet_address"],
246
+ // Payment payout accounts
247
+ user_payout_accounts: ["provider_account_id", "account_holder_name"],
248
+ // Sessions
249
+ sessions: ["token"]
273
250
  };
274
- CoreEventManager.emit(core.CORE_EVENTS.DATABASE.ERROR, payload);
275
- }
276
- /**
277
- * Gets the singleton instance of DbService
278
- *
279
- * @returns {DbService} The singleton instance
280
- */
281
- static getInstance() {
282
- _DbService.instance ??= new _DbService();
283
- return _DbService.instance;
284
- }
285
- /**
286
- * Checks if the database service has been initialized
287
- *
288
- * @returns {boolean} True if initialized
289
- */
290
- static isInitialized() {
291
- return _DbService.instance?.initialized ?? false;
292
- }
293
- /**
294
- * Resets the database service by closing connections and clearing the singleton instance
295
- *
296
- * @description Properly closes the database connection and clears the singleton.
297
- * Useful for testing or when you need to reinitialize with different configuration.
298
- *
299
- * @example
300
- * ```typescript
301
- * await DbService.reset();
302
- * await DbService.initialize({ adapter: 'sql', ... });
303
- * ```
304
- */
305
- static async reset() {
306
- if (_DbService.instance) {
307
- try {
308
- await _DbService.instance.databaseService?.close?.();
309
- } catch {
251
+ DEFAULT_CACHE_TTL_SECONDS = 300;
252
+ DEFAULT_AUDIT_RETENTION_DAYS = 180;
253
+ DEFAULT_POOL_SIZE = 10;
254
+ DEFAULT_CONFIG = {
255
+ adapter: "sql",
256
+ cache: {
257
+ enabled: true,
258
+ provider: "memory",
259
+ ttl: DEFAULT_CACHE_TTL_SECONDS
260
+ },
261
+ softDelete: {
262
+ enabled: true,
263
+ field: "deleted_at",
264
+ excludeTables: [
265
+ "audit_logs",
266
+ "audit.audit_logs",
267
+ "audit.feature_flag_evaluations",
268
+ "feature_flag_evaluations",
269
+ "notification_events",
270
+ "payments_activity",
271
+ "campaign_analytics",
272
+ "admin_actions",
273
+ "backoffice.admin_actions"
274
+ ]
275
+ },
276
+ audit: {
277
+ enabled: true,
278
+ // Enabled by default for compliance
279
+ retentionDays: DEFAULT_AUDIT_RETENTION_DAYS,
280
+ excludeFields: ["password_hash", "api_key_hash", "token_hash"],
281
+ excludeTables: ["audit_logs", "audit.audit_logs"],
282
+ // Don't audit the audit table itself
283
+ schema: "audit",
284
+ // Use dedicated audit schema
285
+ usePartitionedTables: true
286
+ // Use daily partitioned tables (audit_log_yyyy_mm_dd)
310
287
  }
311
- _DbService.instance = null;
312
- }
313
- }
314
- /**
315
- * Initializes the database connection
316
- *
317
- * @description Sets up the database connection using the provided configuration
318
- * or environment variables. This method is idempotent - calling it multiple
319
- * times won't create additional connections.
320
- *
321
- * **Environment Variables:**
322
- * - `DATABASE_URL` - PostgreSQL connection string (for sql/drizzle adapters)
323
- * - `ENCRYPTION_KEY` - 32-byte encryption key for field encryption (optional)
324
- * - `SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`, `SUPABASE_ANON_PUBLIC_KEY` - For supabase adapter
325
- *
326
- * **Encryption:** If `ENCRYPTION_KEY` env is set, encryption is auto-enabled
327
- * using `DEFAULT_ENCRYPTION_FIELDS`. Override via config.encryption.
328
- *
329
- * @param {DbServiceConfig} [config] - Optional configuration
330
- * @returns {Promise<DbService>} The initialized DbService instance
331
- * @throws {DatabasePackageError} When configuration is invalid or connection fails
332
- *
333
- * @example
334
- * ```typescript
335
- * // Minimal - uses DATABASE_URL env, auto-enables encryption if ENCRYPTION_KEY set
336
- * await DbService.initialize();
337
- *
338
- * // With explicit encryption
339
- * await DbService.initialize({
340
- * adapter: 'sql',
341
- * encryption: {
342
- * enabled: true,
343
- * key: 'your-32-byte-encryption-key-here!!',
344
- * fields: DEFAULT_ENCRYPTION_FIELDS,
345
- * },
346
- * });
347
- *
348
- * // Or via Core.initialize() with env:
349
- * await Core.initialize({
350
- * env: { ENCRYPTION_KEY: '...' },
351
- * db: { adapter: 'sql' },
352
- * });
353
- * ```
354
- */
355
- /** Build encryption config from user config */
356
- static buildEncryptionConfig(config) {
357
- const encryption = config?.encryption;
358
- if (!encryption?.key) return void 0;
359
- return {
360
- enabled: encryption.enabled ?? true,
361
- key: encryption.key,
362
- fields: encryption.fields ?? DEFAULT_ENCRYPTION_FIELDS,
363
- algorithm: encryption.algorithm ?? "aes-256-gcm"
364
- };
365
- }
366
- /** Merge user config with defaults */
367
- static mergeConfig(config) {
368
- return {
369
- ...DEFAULT_CONFIG,
370
- ...config,
371
- softDelete: { ...DEFAULT_CONFIG.softDelete, ...config?.softDelete },
372
- cache: { ...DEFAULT_CONFIG.cache, ...config?.cache },
373
- audit: { ...DEFAULT_CONFIG.audit, ...config?.audit },
374
- encryption: _DbService.buildEncryptionConfig(config)
375
- };
376
- }
377
- /** Initialize named adapters */
378
- async initializeNamedAdapters(mergedConfig) {
379
- if (!mergedConfig.adapters) return;
380
- for (const [name, adapterConfig] of Object.entries(mergedConfig.adapters)) {
381
- const namedDbConfig = this.buildDatabaseConfig({
382
- ...mergedConfig,
383
- adapter: adapterConfig.adapter,
384
- drizzle: adapterConfig.drizzle,
385
- supabase: adapterConfig.supabase,
386
- sql: adapterConfig.sql
387
- });
388
- const namedService = await db.createDatabaseService(namedDbConfig);
389
- this.namedAdapters.set(name, namedService);
390
- }
391
- }
392
- static async initialize(config) {
393
- const instance = _DbService.getInstance();
394
- if (instance.initialized) {
395
- return instance;
396
- }
397
- const mergedConfig = _DbService.mergeConfig(config);
398
- instance.config = mergedConfig;
399
- const dbConfig = instance.buildDatabaseConfig(mergedConfig);
400
- instance.databaseService = await db.createDatabaseService(dbConfig);
401
- await instance.initializeNamedAdapters(mergedConfig);
402
- instance.initialized = true;
403
- return instance;
404
- }
405
- /**
406
- * Builds the DatabaseServiceConfig based on the adapter type
407
- * @private
408
- */
409
- /** Builds adapter-specific config based on adapter type */
410
- buildAdapterConfig(adapter, config) {
411
- const builders = {
412
- drizzle: /* @__PURE__ */ __name(() => this.buildDrizzleConfig(config), "drizzle"),
413
- supabase: /* @__PURE__ */ __name(() => this.buildSupabaseConfig(config), "supabase"),
414
- sql: /* @__PURE__ */ __name(() => this.buildSqlConfig(config), "sql")
415
- };
416
- const builder = builders[adapter];
417
- if (!builder) {
418
- throw new errors.DatabasePackageError(
419
- `Unsupported adapter type: ${adapter}`,
420
- errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
421
- );
422
- }
423
- return builder();
424
- }
425
- /** Builds soft delete extension config */
426
- buildSoftDeleteExtension(config) {
427
- if (!config.softDelete?.enabled) return void 0;
428
- return {
429
- enabled: true,
430
- field: config.softDelete.field ?? "deleted_at",
431
- excludeTables: config.softDelete.excludeTables
432
- };
433
- }
434
- /** Builds cache extension config */
435
- buildCacheExtension(config) {
436
- if (!config.cache?.enabled) return void 0;
437
- return {
438
- enabled: true,
439
- ttl: config.cache.ttl ?? DEFAULT_CACHE_TTL_SECONDS,
440
- provider: config.cache.provider ?? "memory",
441
- invalidation: config.cache.invalidation ?? "write"
442
- };
443
- }
444
- /** Builds audit extension config */
445
- buildAuditExtension(config) {
446
- if (!config.audit?.enabled) return void 0;
447
- return {
448
- enabled: true,
449
- retentionDays: config.audit.retentionDays ?? DEFAULT_AUDIT_RETENTION_DAYS,
450
- excludeFields: config.audit.excludeFields,
451
- excludeTables: config.audit.excludeTables,
452
- schema: config.audit.schema ?? "audit",
453
- usePartitionedTables: config.audit.usePartitionedTables ?? true
454
- };
455
- }
456
- /** Builds encryption extension config */
457
- buildEncryptionExtension(config) {
458
- if (!config.encryption?.enabled || !config.encryption.key) return void 0;
459
- return {
460
- enabled: true,
461
- key: config.encryption.key,
462
- fields: config.encryption.fields ?? {},
463
- algorithm: config.encryption.algorithm
464
- };
465
- }
466
- buildDatabaseConfig(config) {
467
- const adapter = config.adapter ?? "drizzle";
468
- const dbConfig = {
469
- adapter,
470
- config: this.buildAdapterConfig(adapter, config)
471
- };
472
- const softDelete = this.buildSoftDeleteExtension(config);
473
- const cache = this.buildCacheExtension(config);
474
- const audit = this.buildAuditExtension(config);
475
- const encryption = this.buildEncryptionExtension(config);
476
- if (softDelete) dbConfig.softDelete = softDelete;
477
- if (cache) dbConfig.cache = cache;
478
- if (audit) dbConfig.audit = audit;
479
- if (encryption) dbConfig.encryption = encryption;
480
- return dbConfig;
481
- }
482
- /**
483
- * Builds Drizzle adapter configuration
484
- * @private
485
- */
486
- buildDrizzleConfig(config) {
487
- const connectionString = config.drizzle?.connectionString;
488
- if (!connectionString) {
489
- throw new errors.DatabasePackageError(
490
- "Drizzle adapter requires a PostgreSQL connection string. Provide `drizzle.connectionString` in config or use Core.initialize() with DATABASE_URL in env. For Supabase, find this in Dashboard > Database > Connection string (URI).",
491
- errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
492
- );
493
- }
494
- const tableIdColumns = this.buildTableIdColumns();
495
- return {
496
- connectionString,
497
- poolSize: config.drizzle?.poolSize ?? DEFAULT_POOL_SIZE,
498
- ssl: config.drizzle?.ssl,
499
- schema: config.drizzle?.schema,
500
- // Pass through schema configuration
501
- tableIdColumns
502
- };
503
- }
504
- /**
505
- * Builds Supabase adapter configuration
506
- * @private
507
- */
508
- // eslint-disable-next-line complexity
509
- buildSupabaseConfig(config) {
510
- const supabaseUrl = config.supabase?.supabaseUrl;
511
- const supabaseServiceKey = config.supabase?.supabaseServiceKey;
512
- const supabaseAnonKey = config.supabase?.supabaseAnonKey;
513
- if (!supabaseUrl || !supabaseServiceKey) {
514
- throw new errors.DatabasePackageError(
515
- "Supabase adapter requires supabaseUrl and supabaseServiceKey. Provide in config or use Core.initialize() with SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY in env.",
516
- errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
517
- );
518
- }
519
- if (!supabaseAnonKey) {
520
- throw new errors.DatabasePackageError(
521
- "Supabase adapter requires supabaseAnonKey. Provide in config or use Core.initialize() with SUPABASE_ANON_PUBLIC_KEY in env.",
522
- errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
523
- );
524
- }
525
- const tableIdColumns = this.buildTableIdColumns();
526
- return {
527
- supabaseUrl,
528
- supabaseServiceKey,
529
- supabaseAnonKey,
530
- schema: config.supabase?.schema ?? "public",
531
- tableIdColumns
288
+ // NOTE: Encryption requires key from environment or DbService.initialize()
289
+ // Enable via: DbService.initialize({ encryption: { enabled: true, key: process.env.ENCRYPTION_KEY!, fields: DEFAULT_ENCRYPTION_FIELDS } })
532
290
  };
533
- }
534
- /**
535
- * Builds SQL adapter configuration
536
- * @private
537
- */
538
- buildSqlConfig(config) {
539
- const connectionString = config.sql?.connectionString;
540
- if (!connectionString) {
541
- throw new errors.DatabasePackageError(
542
- "SQL adapter requires a connection string. Provide `sql.connectionString` in config or use Core.initialize() with DATABASE_URL in env.",
543
- errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
544
- );
545
- }
546
- const tableIdColumns = this.buildTableIdColumns();
547
- return {
548
- connectionString,
549
- dialect: config.sql?.dialect ?? "postgresql",
550
- schema: config.sql?.schema,
551
- // Pass through schema configuration
552
- tableIdColumns
291
+ TABLE_REGISTRY = {
292
+ // Migration 008: Feature Flags (custom ID column 'key')
293
+ feature_flags: { idColumn: "key" },
294
+ // Test Feature Flags (custom ID column 'key')
295
+ test_feature_flags: { idColumn: "key" },
296
+ // Migration 010: Universal Analytics (custom ID column 'user_id')
297
+ user_analytics_summary: { idColumn: "user_id" }
553
298
  };
554
- }
555
- /**
556
- * Builds table ID column mappings from TABLE_REGISTRY
557
- * @private
558
- * @returns Record of table names to ID column names
559
- */
560
- // eslint-disable-next-line complexity
561
- buildTableIdColumns() {
562
- const tableIdColumns = {};
563
- const baseNameConflicts = /* @__PURE__ */ new Set();
564
- const baseNameMappings = /* @__PURE__ */ new Map();
565
- for (const tableName of Object.keys(TABLE_REGISTRY)) {
566
- if (tableName.includes(".")) {
567
- const baseName = tableName.split(".").pop();
568
- if (baseNameMappings.has(baseName) || TABLE_REGISTRY[baseName]) {
569
- baseNameConflicts.add(baseName);
570
- } else {
571
- baseNameMappings.set(baseName, tableName);
299
+ DbService = class _DbService {
300
+ constructor() {
301
+ this.databaseService = null;
302
+ this.namedAdapters = /* @__PURE__ */ new Map();
303
+ this.config = null;
304
+ this.initialized = false;
305
+ }
306
+ static {
307
+ __name(this, "DbService");
308
+ }
309
+ static {
310
+ this.instance = null;
311
+ }
312
+ /**
313
+ * Emits a database error event via CoreEventManager.
314
+ * Called when database operations fail to integrate with global error handling.
315
+ *
316
+ * @param error - The error that occurred
317
+ * @param operation - The operation that failed (e.g., 'transaction', 'query', 'healthCheck')
318
+ * @param table - Optional table name involved in the operation
319
+ * @param query - Optional query string that failed
320
+ * @param recoverable - Whether the error is recoverable (default: false)
321
+ */
322
+ emitDatabaseError(error, operation, options) {
323
+ const payload = {
324
+ error,
325
+ operation,
326
+ table: options?.table,
327
+ query: options?.query,
328
+ recoverable: options?.recoverable ?? false
329
+ };
330
+ CoreEventManager.emit(core.CORE_EVENTS.DATABASE.ERROR, payload);
331
+ }
332
+ /**
333
+ * Gets the singleton instance of DbService
334
+ *
335
+ * @returns {DbService} The singleton instance
336
+ */
337
+ static getInstance() {
338
+ _DbService.instance ??= new _DbService();
339
+ return _DbService.instance;
340
+ }
341
+ /**
342
+ * Checks if the database service has been initialized
343
+ *
344
+ * @returns {boolean} True if initialized
345
+ */
346
+ static isInitialized() {
347
+ return _DbService.instance?.initialized ?? false;
348
+ }
349
+ /**
350
+ * Resets the database service by closing connections and clearing the singleton instance
351
+ *
352
+ * @description Properly closes the database connection and clears the singleton.
353
+ * Useful for testing or when you need to reinitialize with different configuration.
354
+ *
355
+ * @example
356
+ * ```typescript
357
+ * await DbService.reset();
358
+ * await DbService.initialize({ adapter: 'sql', ... });
359
+ * ```
360
+ */
361
+ static async reset() {
362
+ if (_DbService.instance) {
363
+ try {
364
+ await _DbService.instance.databaseService?.close?.();
365
+ } catch {
366
+ }
367
+ _DbService.instance = null;
572
368
  }
573
369
  }
574
- }
575
- for (const [tableName, config] of Object.entries(TABLE_REGISTRY)) {
576
- const idColumn = config.idColumn ?? "id";
577
- if (idColumn === "id") continue;
578
- tableIdColumns[tableName] = idColumn;
579
- if (!tableName.includes(".")) continue;
580
- const baseName = tableName.split(".").pop();
581
- if (!baseNameConflicts.has(baseName)) {
582
- tableIdColumns[baseName] = idColumn;
370
+ /**
371
+ * Initializes the database connection
372
+ *
373
+ * @description Sets up the database connection using the provided configuration
374
+ * or environment variables. This method is idempotent - calling it multiple
375
+ * times won't create additional connections.
376
+ *
377
+ * **Environment Variables:**
378
+ * - `DATABASE_URL` - PostgreSQL connection string (for sql/drizzle adapters)
379
+ * - `ENCRYPTION_KEY` - 32-byte encryption key for field encryption (optional)
380
+ * - `SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`, `SUPABASE_ANON_PUBLIC_KEY` - For supabase adapter
381
+ *
382
+ * **Encryption:** If `ENCRYPTION_KEY` env is set, encryption is auto-enabled
383
+ * using `DEFAULT_ENCRYPTION_FIELDS`. Override via config.encryption.
384
+ *
385
+ * @param {DbServiceConfig} [config] - Optional configuration
386
+ * @returns {Promise<DbService>} The initialized DbService instance
387
+ * @throws {DatabasePackageError} When configuration is invalid or connection fails
388
+ *
389
+ * @example
390
+ * ```typescript
391
+ * // Minimal - uses DATABASE_URL env, auto-enables encryption if ENCRYPTION_KEY set
392
+ * await DbService.initialize();
393
+ *
394
+ * // With explicit encryption
395
+ * await DbService.initialize({
396
+ * adapter: 'sql',
397
+ * encryption: {
398
+ * enabled: true,
399
+ * key: 'your-32-byte-encryption-key-here!!',
400
+ * fields: DEFAULT_ENCRYPTION_FIELDS,
401
+ * },
402
+ * });
403
+ *
404
+ * // Or via Core.initialize() with env:
405
+ * await Core.initialize({
406
+ * env: { ENCRYPTION_KEY: '...' },
407
+ * db: { adapter: 'sql' },
408
+ * });
409
+ * ```
410
+ */
411
+ /** Build encryption config from user config */
412
+ static buildEncryptionConfig(config) {
413
+ const encryption = config?.encryption;
414
+ if (!encryption?.key) return void 0;
415
+ return {
416
+ enabled: encryption.enabled ?? true,
417
+ key: encryption.key,
418
+ fields: encryption.fields ?? DEFAULT_ENCRYPTION_FIELDS,
419
+ algorithm: encryption.algorithm ?? "aes-256-gcm"
420
+ };
583
421
  }
584
- }
585
- return tableIdColumns;
586
- }
587
- /**
588
- * Gets the initialized database service instance
589
- *
590
- * @param {string} [adapterName] - Optional named adapter to use instead of default
591
- * @returns {DatabaseServiceInterface} The database service instance
592
- * @throws {DatabasePackageError} When database is not initialized or named adapter not found
593
- */
594
- getDatabase(adapterName) {
595
- if (adapterName) {
596
- const namedAdapter = this.namedAdapters.get(adapterName);
597
- if (!namedAdapter) {
598
- throw new errors.DatabasePackageError(
599
- `Named adapter '${adapterName}' not found. Available adapters: ${Array.from(this.namedAdapters.keys()).join(", ")}`,
600
- errors$1.DATABASE_ERROR_CODES.INIT_FAILED
601
- );
422
+ /** Merge user config with defaults */
423
+ static mergeConfig(config) {
424
+ return {
425
+ ...DEFAULT_CONFIG,
426
+ ...config,
427
+ softDelete: { ...DEFAULT_CONFIG.softDelete, ...config?.softDelete },
428
+ cache: { ...DEFAULT_CONFIG.cache, ...config?.cache },
429
+ audit: { ...DEFAULT_CONFIG.audit, ...config?.audit },
430
+ encryption: _DbService.buildEncryptionConfig(config)
431
+ };
602
432
  }
603
- return namedAdapter;
604
- }
605
- if (!this.databaseService) {
606
- throw new errors.DatabasePackageError(
607
- "Database not initialized. Call DbService.initialize() first.",
608
- errors$1.DATABASE_ERROR_CODES.INIT_FAILED
609
- );
610
- }
611
- return this.databaseService;
612
- }
613
- /**
614
- * Gets a named adapter by name
615
- *
616
- * @param {string} name - The name of the adapter
617
- * @returns {DatabaseServiceInterface} The named adapter instance
618
- * @throws {DatabasePackageError} When adapter not found
619
- */
620
- getAdapter(name) {
621
- return this.getDatabase(name);
622
- }
623
- /**
624
- * Lists all available named adapters
625
- *
626
- * @returns {string[]} Array of adapter names
627
- */
628
- getAvailableAdapters() {
629
- return ["default", ...Array.from(this.namedAdapters.keys())];
630
- }
631
- /**
632
- * Executes a database transaction with automatic rollback on failure
633
- *
634
- * @template T The return type of the transaction callback
635
- * @param {Function} callback - Function that receives transaction object
636
- * @returns {Promise<T>} The result of the transaction callback
637
- * @throws {DatabasePackageError} When transaction fails
638
- */
639
- async transaction(callback) {
640
- const db = this.getDatabase();
641
- const result = await db.transaction(callback);
642
- if (!result.success) {
643
- const errorMessage = result.error?.message ?? "Transaction failed";
644
- const error = new errors.DatabasePackageError(errorMessage, errors$1.DATABASE_ERROR_CODES.TRANSACTION_FAILED);
645
- this.emitDatabaseError(error, "transaction", { recoverable: true });
646
- throw error;
647
- }
648
- if (result.value === void 0 || result.value === null) {
649
- const error = new errors.DatabasePackageError(
650
- "Transaction returned no value",
651
- errors$1.DATABASE_ERROR_CODES.INVALID_RESULT
652
- );
653
- this.emitDatabaseError(error, "transaction", { recoverable: false });
654
- throw error;
655
- }
656
- return result.value;
657
- }
658
- /**
659
- * Sets audit context for subsequent operations
660
- *
661
- * @param context - Audit context (userId, requestId, etc.)
662
- */
663
- async setAuditContext(context) {
664
- const db = this.getDatabase();
665
- await db.setAuditContext(context);
666
- }
667
- /**
668
- * Performs a health check on the database connection
669
- *
670
- * @returns Health check result
671
- */
672
- async healthCheck() {
673
- try {
674
- const db = this.getDatabase();
675
- const result = await db.healthCheck();
676
- if (result.success && result.value) {
433
+ /** Initialize named adapters */
434
+ async initializeNamedAdapters(mergedConfig) {
435
+ if (!mergedConfig.adapters) return;
436
+ for (const [name, adapterConfig] of Object.entries(mergedConfig.adapters)) {
437
+ const namedDbConfig = this.buildDatabaseConfig({
438
+ ...mergedConfig,
439
+ adapter: adapterConfig.adapter,
440
+ drizzle: adapterConfig.drizzle,
441
+ supabase: adapterConfig.supabase,
442
+ sql: adapterConfig.sql
443
+ });
444
+ const namedService = await db.createDatabaseService(namedDbConfig);
445
+ this.namedAdapters.set(name, namedService);
446
+ }
447
+ }
448
+ static async initialize(config) {
449
+ const instance = _DbService.getInstance();
450
+ if (instance.initialized) {
451
+ return instance;
452
+ }
453
+ const mergedConfig = _DbService.mergeConfig(config);
454
+ instance.config = mergedConfig;
455
+ const dbConfig = instance.buildDatabaseConfig(mergedConfig);
456
+ instance.databaseService = await db.createDatabaseService(dbConfig);
457
+ await instance.initializeNamedAdapters(mergedConfig);
458
+ instance.initialized = true;
459
+ return instance;
460
+ }
461
+ /**
462
+ * Builds the DatabaseServiceConfig based on the adapter type
463
+ * @private
464
+ */
465
+ /** Builds adapter-specific config based on adapter type */
466
+ buildAdapterConfig(adapter, config) {
467
+ const builders = {
468
+ drizzle: /* @__PURE__ */ __name(() => this.buildDrizzleConfig(config), "drizzle"),
469
+ supabase: /* @__PURE__ */ __name(() => this.buildSupabaseConfig(config), "supabase"),
470
+ sql: /* @__PURE__ */ __name(() => this.buildSqlConfig(config), "sql")
471
+ };
472
+ const builder = builders[adapter];
473
+ if (!builder) {
474
+ throw new errors.DatabasePackageError(
475
+ `Unsupported adapter type: ${adapter}`,
476
+ errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
477
+ );
478
+ }
479
+ return builder();
480
+ }
481
+ /** Builds soft delete extension config */
482
+ buildSoftDeleteExtension(config) {
483
+ if (!config.softDelete?.enabled) return void 0;
677
484
  return {
678
- isHealthy: result.value.isHealthy,
679
- responseTime: result.value.responseTime
485
+ enabled: true,
486
+ field: config.softDelete.field ?? "deleted_at",
487
+ excludeTables: config.softDelete.excludeTables
680
488
  };
681
489
  }
682
- const errorMessage = result.error?.message ?? "Health check failed";
683
- this.emitDatabaseError(
684
- new errors.DatabasePackageError(errorMessage, errors$1.DATABASE_ERROR_CODES.CONNECTION_ERROR),
685
- "healthCheck",
686
- { recoverable: true }
687
- );
688
- return {
689
- isHealthy: false,
690
- error: errorMessage
691
- };
692
- } catch (error) {
693
- this.emitDatabaseError(error, "healthCheck", { recoverable: true });
694
- return {
695
- isHealthy: false,
696
- error: error instanceof Error ? error.message : "Unknown error"
697
- };
698
- }
699
- }
700
- /**
701
- * Gets the table registry with all known tables and their ID columns
702
- *
703
- * @returns The complete table registry
704
- */
705
- static getTableRegistry() {
706
- return TABLE_REGISTRY;
707
- }
708
- /**
709
- * Gets ID column for a specific table
710
- *
711
- * @param tableName - Name of the table
712
- * @returns ID column name or 'id' as default
713
- */
714
- static getTableIdColumn(tableName) {
715
- return TABLE_REGISTRY[tableName]?.idColumn ?? "id";
716
- }
717
- /**
718
- * Reinitializes the database connection with new config
719
- *
720
- * @param {DbServiceConfig} [config] - New configuration
721
- * @returns {Promise<DbService>} The reinitialized DbService instance
722
- */
723
- static async reinitialize(config) {
724
- const instance = _DbService.getInstance();
725
- await instance.close();
726
- instance.initialized = false;
727
- return _DbService.initialize(config);
728
- }
729
- /**
730
- * Closes the database connection and cleans up resources
731
- */
732
- async close() {
733
- this.databaseService = null;
734
- this.initialized = false;
735
- this.config = null;
736
- }
737
- /**
738
- * Gets the current configuration
739
- *
740
- * @returns {DbServiceConfig | null} Current config or null if not initialized
741
- */
742
- getConfig() {
743
- return this.config;
744
- }
745
- /**
746
- * Gets the current adapter type
747
- *
748
- * @returns The adapter type or null if not initialized
749
- */
750
- getAdapterType() {
751
- return this.config?.adapter ?? null;
490
+ /** Builds cache extension config */
491
+ buildCacheExtension(config) {
492
+ if (!config.cache?.enabled) return void 0;
493
+ return {
494
+ enabled: true,
495
+ ttl: config.cache.ttl ?? DEFAULT_CACHE_TTL_SECONDS,
496
+ provider: config.cache.provider ?? "memory",
497
+ invalidation: config.cache.invalidation ?? "write"
498
+ };
499
+ }
500
+ /** Builds audit extension config */
501
+ buildAuditExtension(config) {
502
+ if (!config.audit?.enabled) return void 0;
503
+ return {
504
+ enabled: true,
505
+ retentionDays: config.audit.retentionDays ?? DEFAULT_AUDIT_RETENTION_DAYS,
506
+ excludeFields: config.audit.excludeFields,
507
+ excludeTables: config.audit.excludeTables,
508
+ schema: config.audit.schema ?? "audit",
509
+ usePartitionedTables: config.audit.usePartitionedTables ?? true
510
+ };
511
+ }
512
+ /** Builds encryption extension config */
513
+ buildEncryptionExtension(config) {
514
+ if (!config.encryption?.enabled || !config.encryption.key) return void 0;
515
+ return {
516
+ enabled: true,
517
+ key: config.encryption.key,
518
+ fields: config.encryption.fields ?? {},
519
+ algorithm: config.encryption.algorithm
520
+ };
521
+ }
522
+ buildDatabaseConfig(config) {
523
+ const adapter = config.adapter ?? "drizzle";
524
+ const dbConfig = {
525
+ adapter,
526
+ config: this.buildAdapterConfig(adapter, config)
527
+ };
528
+ const softDelete = this.buildSoftDeleteExtension(config);
529
+ const cache = this.buildCacheExtension(config);
530
+ const audit = this.buildAuditExtension(config);
531
+ const encryption = this.buildEncryptionExtension(config);
532
+ if (softDelete) dbConfig.softDelete = softDelete;
533
+ if (cache) dbConfig.cache = cache;
534
+ if (audit) dbConfig.audit = audit;
535
+ if (encryption) dbConfig.encryption = encryption;
536
+ return dbConfig;
537
+ }
538
+ /**
539
+ * Builds Drizzle adapter configuration
540
+ * @private
541
+ */
542
+ buildDrizzleConfig(config) {
543
+ const connectionString = config.drizzle?.connectionString;
544
+ if (!connectionString) {
545
+ throw new errors.DatabasePackageError(
546
+ "Drizzle adapter requires a PostgreSQL connection string. Provide `drizzle.connectionString` in config or use Core.initialize() with DATABASE_URL in env. For Supabase, find this in Dashboard > Database > Connection string (URI).",
547
+ errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
548
+ );
549
+ }
550
+ const tableIdColumns = this.buildTableIdColumns();
551
+ return {
552
+ connectionString,
553
+ poolSize: config.drizzle?.poolSize ?? DEFAULT_POOL_SIZE,
554
+ ssl: config.drizzle?.ssl,
555
+ schema: config.drizzle?.schema,
556
+ // Pass through schema configuration
557
+ tableIdColumns
558
+ };
559
+ }
560
+ /**
561
+ * Builds Supabase adapter configuration
562
+ * @private
563
+ */
564
+ // eslint-disable-next-line complexity
565
+ buildSupabaseConfig(config) {
566
+ const supabaseUrl = config.supabase?.supabaseUrl;
567
+ const supabaseServiceKey = config.supabase?.supabaseServiceKey;
568
+ const supabaseAnonKey = config.supabase?.supabaseAnonKey;
569
+ if (!supabaseUrl || !supabaseServiceKey) {
570
+ throw new errors.DatabasePackageError(
571
+ "Supabase adapter requires supabaseUrl and supabaseServiceKey. Provide in config or use Core.initialize() with SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY in env.",
572
+ errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
573
+ );
574
+ }
575
+ if (!supabaseAnonKey) {
576
+ throw new errors.DatabasePackageError(
577
+ "Supabase adapter requires supabaseAnonKey. Provide in config or use Core.initialize() with SUPABASE_ANON_PUBLIC_KEY in env.",
578
+ errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
579
+ );
580
+ }
581
+ const tableIdColumns = this.buildTableIdColumns();
582
+ return {
583
+ supabaseUrl,
584
+ supabaseServiceKey,
585
+ supabaseAnonKey,
586
+ schema: config.supabase?.schema ?? "public",
587
+ tableIdColumns
588
+ };
589
+ }
590
+ /**
591
+ * Builds SQL adapter configuration
592
+ * @private
593
+ */
594
+ buildSqlConfig(config) {
595
+ const connectionString = config.sql?.connectionString;
596
+ if (!connectionString) {
597
+ throw new errors.DatabasePackageError(
598
+ "SQL adapter requires a connection string. Provide `sql.connectionString` in config or use Core.initialize() with DATABASE_URL in env.",
599
+ errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
600
+ );
601
+ }
602
+ const tableIdColumns = this.buildTableIdColumns();
603
+ return {
604
+ connectionString,
605
+ dialect: config.sql?.dialect ?? "postgresql",
606
+ schema: config.sql?.schema,
607
+ // Pass through schema configuration
608
+ tableIdColumns
609
+ };
610
+ }
611
+ /**
612
+ * Builds table ID column mappings from TABLE_REGISTRY
613
+ * @private
614
+ * @returns Record of table names to ID column names
615
+ */
616
+ // eslint-disable-next-line complexity
617
+ buildTableIdColumns() {
618
+ const tableIdColumns = {};
619
+ const baseNameConflicts = /* @__PURE__ */ new Set();
620
+ const baseNameMappings = /* @__PURE__ */ new Map();
621
+ for (const tableName of Object.keys(TABLE_REGISTRY)) {
622
+ if (tableName.includes(".")) {
623
+ const baseName = tableName.split(".").pop();
624
+ if (baseNameMappings.has(baseName) || TABLE_REGISTRY[baseName]) {
625
+ baseNameConflicts.add(baseName);
626
+ } else {
627
+ baseNameMappings.set(baseName, tableName);
628
+ }
629
+ }
630
+ }
631
+ for (const [tableName, config] of Object.entries(TABLE_REGISTRY)) {
632
+ const idColumn = config.idColumn ?? "id";
633
+ if (idColumn === "id") continue;
634
+ tableIdColumns[tableName] = idColumn;
635
+ if (!tableName.includes(".")) continue;
636
+ const baseName = tableName.split(".").pop();
637
+ if (!baseNameConflicts.has(baseName)) {
638
+ tableIdColumns[baseName] = idColumn;
639
+ }
640
+ }
641
+ return tableIdColumns;
642
+ }
643
+ /**
644
+ * Gets the initialized database service instance
645
+ *
646
+ * @param {string} [adapterName] - Optional named adapter to use instead of default
647
+ * @returns {DatabaseServiceInterface} The database service instance
648
+ * @throws {DatabasePackageError} When database is not initialized or named adapter not found
649
+ */
650
+ getDatabase(adapterName) {
651
+ if (adapterName) {
652
+ const namedAdapter = this.namedAdapters.get(adapterName);
653
+ if (!namedAdapter) {
654
+ throw new errors.DatabasePackageError(
655
+ `Named adapter '${adapterName}' not found. Available adapters: ${Array.from(this.namedAdapters.keys()).join(", ")}`,
656
+ errors$1.DATABASE_ERROR_CODES.INIT_FAILED
657
+ );
658
+ }
659
+ return namedAdapter;
660
+ }
661
+ if (!this.databaseService) {
662
+ throw new errors.DatabasePackageError(
663
+ "Database not initialized. Call DbService.initialize() first.",
664
+ errors$1.DATABASE_ERROR_CODES.INIT_FAILED
665
+ );
666
+ }
667
+ return this.databaseService;
668
+ }
669
+ /**
670
+ * Gets a named adapter by name
671
+ *
672
+ * @param {string} name - The name of the adapter
673
+ * @returns {DatabaseServiceInterface} The named adapter instance
674
+ * @throws {DatabasePackageError} When adapter not found
675
+ */
676
+ getAdapter(name) {
677
+ return this.getDatabase(name);
678
+ }
679
+ /**
680
+ * Lists all available named adapters
681
+ *
682
+ * @returns {string[]} Array of adapter names
683
+ */
684
+ getAvailableAdapters() {
685
+ return ["default", ...Array.from(this.namedAdapters.keys())];
686
+ }
687
+ /**
688
+ * Executes a database transaction with automatic rollback on failure
689
+ *
690
+ * @template T The return type of the transaction callback
691
+ * @param {Function} callback - Function that receives transaction object
692
+ * @returns {Promise<T>} The result of the transaction callback
693
+ * @throws {DatabasePackageError} When transaction fails
694
+ */
695
+ async transaction(callback) {
696
+ const db = this.getDatabase();
697
+ const result = await db.transaction(callback);
698
+ if (!result.success) {
699
+ const errorMessage = result.error?.message ?? "Transaction failed";
700
+ const error = new errors.DatabasePackageError(errorMessage, errors$1.DATABASE_ERROR_CODES.TRANSACTION_FAILED);
701
+ this.emitDatabaseError(error, "transaction", { recoverable: true });
702
+ throw error;
703
+ }
704
+ if (result.value === void 0 || result.value === null) {
705
+ const error = new errors.DatabasePackageError(
706
+ "Transaction returned no value",
707
+ errors$1.DATABASE_ERROR_CODES.INVALID_RESULT
708
+ );
709
+ this.emitDatabaseError(error, "transaction", { recoverable: false });
710
+ throw error;
711
+ }
712
+ return result.value;
713
+ }
714
+ /**
715
+ * Sets audit context for subsequent operations
716
+ *
717
+ * @param context - Audit context (userId, requestId, etc.)
718
+ */
719
+ async setAuditContext(context) {
720
+ const db = this.getDatabase();
721
+ await db.setAuditContext(context);
722
+ }
723
+ /**
724
+ * Performs a health check on the database connection
725
+ *
726
+ * @returns Health check result
727
+ */
728
+ async healthCheck() {
729
+ try {
730
+ const db = this.getDatabase();
731
+ const result = await db.healthCheck();
732
+ if (result.success && result.value) {
733
+ return {
734
+ isHealthy: result.value.isHealthy,
735
+ responseTime: result.value.responseTime
736
+ };
737
+ }
738
+ const errorMessage = result.error?.message ?? "Health check failed";
739
+ this.emitDatabaseError(
740
+ new errors.DatabasePackageError(errorMessage, errors$1.DATABASE_ERROR_CODES.CONNECTION_ERROR),
741
+ "healthCheck",
742
+ { recoverable: true }
743
+ );
744
+ return {
745
+ isHealthy: false,
746
+ error: errorMessage
747
+ };
748
+ } catch (error) {
749
+ this.emitDatabaseError(error, "healthCheck", { recoverable: true });
750
+ return {
751
+ isHealthy: false,
752
+ error: error instanceof Error ? error.message : "Unknown error"
753
+ };
754
+ }
755
+ }
756
+ /**
757
+ * Gets the table registry with all known tables and their ID columns
758
+ *
759
+ * @returns The complete table registry
760
+ */
761
+ static getTableRegistry() {
762
+ return TABLE_REGISTRY;
763
+ }
764
+ /**
765
+ * Gets ID column for a specific table
766
+ *
767
+ * @param tableName - Name of the table
768
+ * @returns ID column name or 'id' as default
769
+ */
770
+ static getTableIdColumn(tableName) {
771
+ return TABLE_REGISTRY[tableName]?.idColumn ?? "id";
772
+ }
773
+ /**
774
+ * Reinitializes the database connection with new config
775
+ *
776
+ * @param {DbServiceConfig} [config] - New configuration
777
+ * @returns {Promise<DbService>} The reinitialized DbService instance
778
+ */
779
+ static async reinitialize(config) {
780
+ const instance = _DbService.getInstance();
781
+ await instance.close();
782
+ instance.initialized = false;
783
+ return _DbService.initialize(config);
784
+ }
785
+ /**
786
+ * Closes the database connection and cleans up resources
787
+ */
788
+ async close() {
789
+ this.databaseService = null;
790
+ this.initialized = false;
791
+ this.config = null;
792
+ }
793
+ /**
794
+ * Gets the current configuration
795
+ *
796
+ * @returns {DbServiceConfig | null} Current config or null if not initialized
797
+ */
798
+ getConfig() {
799
+ return this.config;
800
+ }
801
+ /**
802
+ * Gets the current adapter type
803
+ *
804
+ * @returns The adapter type or null if not initialized
805
+ */
806
+ getAdapterType() {
807
+ return this.config?.adapter ?? null;
808
+ }
809
+ /**
810
+ * Creates a dedicated database service instance (NOT the singleton)
811
+ *
812
+ * Use this when you need an isolated database connection with its own configuration
813
+ * that doesn't affect or get affected by the shared singleton instance.
814
+ *
815
+ * @param config - Database service configuration
816
+ * @returns Promise that resolves to a new dedicated DbService instance
817
+ *
818
+ * @example
819
+ * ```typescript
820
+ * // Create a dedicated instance for analytics database
821
+ * const analyticsDb = await DbService.createInstance({
822
+ * adapter: 'sql',
823
+ * sql: { connectionString: process.env.ANALYTICS_DB_URL },
824
+ * cache: { enabled: false }, // No caching for analytics
825
+ * });
826
+ *
827
+ * // This instance is independent from DbService.getInstance()
828
+ * const data = await analyticsDb.getDatabase().list('events');
829
+ *
830
+ * // Clean up when done
831
+ * await analyticsDb.close();
832
+ * ```
833
+ */
834
+ // eslint-disable-next-line complexity
835
+ static async createInstance(config) {
836
+ const dedicatedInstance = new _DbService();
837
+ const encryptionKey = config.encryption?.key;
838
+ const encryptionConfig = encryptionKey ? {
839
+ enabled: config.encryption?.enabled ?? true,
840
+ key: encryptionKey,
841
+ fields: config.encryption?.fields ?? DEFAULT_ENCRYPTION_FIELDS,
842
+ algorithm: config.encryption?.algorithm ?? "aes-256-gcm"
843
+ } : void 0;
844
+ const mergedConfig = {
845
+ ...DEFAULT_CONFIG,
846
+ ...config,
847
+ // Deep merge extensions
848
+ softDelete: { ...DEFAULT_CONFIG.softDelete, ...config.softDelete },
849
+ cache: { ...DEFAULT_CONFIG.cache, ...config.cache },
850
+ audit: { ...DEFAULT_CONFIG.audit, ...config.audit },
851
+ // Encryption only if key is available
852
+ encryption: encryptionConfig
853
+ };
854
+ dedicatedInstance.config = mergedConfig;
855
+ const dbConfig = dedicatedInstance.buildDatabaseConfig(mergedConfig);
856
+ dedicatedInstance.databaseService = await db.createDatabaseService(dbConfig);
857
+ if (mergedConfig.adapters) {
858
+ for (const [name, adapterConfig] of Object.entries(mergedConfig.adapters)) {
859
+ const namedDbConfig = dedicatedInstance.buildDatabaseConfig({
860
+ ...mergedConfig,
861
+ adapter: adapterConfig.adapter,
862
+ drizzle: adapterConfig.drizzle,
863
+ supabase: adapterConfig.supabase,
864
+ sql: adapterConfig.sql
865
+ });
866
+ const namedService = await db.createDatabaseService(namedDbConfig);
867
+ dedicatedInstance.namedAdapters.set(name, namedService);
868
+ }
869
+ }
870
+ dedicatedInstance.initialized = true;
871
+ return dedicatedInstance;
872
+ }
873
+ };
752
874
  }
753
- /**
754
- * Creates a dedicated database service instance (NOT the singleton)
755
- *
756
- * Use this when you need an isolated database connection with its own configuration
757
- * that doesn't affect or get affected by the shared singleton instance.
758
- *
759
- * @param config - Database service configuration
760
- * @returns Promise that resolves to a new dedicated DbService instance
761
- *
762
- * @example
763
- * ```typescript
764
- * // Create a dedicated instance for analytics database
765
- * const analyticsDb = await DbService.createInstance({
766
- * adapter: 'sql',
767
- * sql: { connectionString: process.env.ANALYTICS_DB_URL },
768
- * cache: { enabled: false }, // No caching for analytics
769
- * });
770
- *
771
- * // This instance is independent from DbService.getInstance()
772
- * const data = await analyticsDb.getDatabase().list('events');
773
- *
774
- * // Clean up when done
775
- * await analyticsDb.close();
776
- * ```
777
- */
778
- // eslint-disable-next-line complexity
779
- static async createInstance(config) {
780
- const dedicatedInstance = new _DbService();
781
- const encryptionKey = config.encryption?.key;
782
- const encryptionConfig = encryptionKey ? {
783
- enabled: config.encryption?.enabled ?? true,
784
- key: encryptionKey,
785
- fields: config.encryption?.fields ?? DEFAULT_ENCRYPTION_FIELDS,
786
- algorithm: config.encryption?.algorithm ?? "aes-256-gcm"
787
- } : void 0;
788
- const mergedConfig = {
789
- ...DEFAULT_CONFIG,
790
- ...config,
791
- // Deep merge extensions
792
- softDelete: { ...DEFAULT_CONFIG.softDelete, ...config.softDelete },
793
- cache: { ...DEFAULT_CONFIG.cache, ...config.cache },
794
- audit: { ...DEFAULT_CONFIG.audit, ...config.audit },
795
- // Encryption only if key is available
796
- encryption: encryptionConfig
875
+ });
876
+
877
+ // src/services/StorageService.ts
878
+ var StorageService_exports = {};
879
+ __export(StorageService_exports, {
880
+ StorageService: () => StorageService
881
+ });
882
+ var StorageService;
883
+ var init_StorageService = __esm({
884
+ "src/services/StorageService.ts"() {
885
+ init_CoreEventManager();
886
+ StorageService = class _StorageService {
887
+ constructor() {
888
+ this.storageService = null;
889
+ this.config = null;
890
+ this.initialized = false;
891
+ }
892
+ static {
893
+ __name(this, "StorageService");
894
+ }
895
+ static {
896
+ this.instance = null;
897
+ }
898
+ // ─────────────────────────────────────────────────────────────────
899
+ // Error Handling
900
+ // ─────────────────────────────────────────────────────────────────
901
+ /**
902
+ * Emits a storage error event via CoreEventManager.
903
+ * Called when storage operations fail to integrate with global error handling.
904
+ */
905
+ emitStorageError(error, operation, options) {
906
+ const payload = {
907
+ error,
908
+ operation,
909
+ fileId: options?.fileId,
910
+ filename: options?.filename,
911
+ recoverable: options?.recoverable ?? false
912
+ };
913
+ CoreEventManager.emit(core.CORE_EVENTS.STORAGE.ERROR, payload);
914
+ }
915
+ // ─────────────────────────────────────────────────────────────────
916
+ // Singleton Management
917
+ // ─────────────────────────────────────────────────────────────────
918
+ /**
919
+ * Gets the singleton instance of StorageService
920
+ */
921
+ static getInstance() {
922
+ _StorageService.instance ??= new _StorageService();
923
+ return _StorageService.instance;
924
+ }
925
+ /**
926
+ * Checks if the storage service has been initialized
927
+ */
928
+ static isInitialized() {
929
+ return _StorageService.instance?.initialized ?? false;
930
+ }
931
+ /**
932
+ * Resets the storage service by clearing the singleton instance
933
+ */
934
+ static async reset() {
935
+ if (_StorageService.instance) {
936
+ _StorageService.instance.storageService = null;
937
+ _StorageService.instance.config = null;
938
+ _StorageService.instance.initialized = false;
939
+ _StorageService.instance = null;
940
+ }
941
+ }
942
+ /**
943
+ * Initializes the storage service
944
+ *
945
+ * @param config - Storage service configuration
946
+ * @returns The initialized StorageService instance
947
+ */
948
+ static async initialize(config) {
949
+ const instance = _StorageService.getInstance();
950
+ if (instance.initialized) {
951
+ return instance;
952
+ }
953
+ instance.config = config;
954
+ instance.storageService = new storage.StorageService(config);
955
+ instance.initialized = true;
956
+ return instance;
957
+ }
958
+ /**
959
+ * Gets the raw underlying storage service instance without error handling wrapper.
960
+ * Use this only if you need direct access to the underlying service.
961
+ *
962
+ * @returns The raw StorageService instance from @plyaz/storage
963
+ * @throws {StoragePackageError} When storage is not initialized
964
+ */
965
+ getRawStorage() {
966
+ if (!this.storageService) {
967
+ throw new errors.StoragePackageError(
968
+ "Storage not initialized. Call StorageService.initialize() first or use Core.initialize() with storage config.",
969
+ errors$1.STORAGE_ERROR_CODES.INITIALIZATION_FAILED
970
+ );
971
+ }
972
+ return this.storageService;
973
+ }
974
+ // ─────────────────────────────────────────────────────────────────
975
+ // Service Access
976
+ // ─────────────────────────────────────────────────────────────────
977
+ /**
978
+ * Gets the storage service with automatic error handling.
979
+ * All method calls are wrapped with try/catch and emit error events on failure.
980
+ * Any method added to @plyaz/storage will be automatically available.
981
+ *
982
+ * @example
983
+ * ```typescript
984
+ * const storage = StorageService.getInstance().getStorage();
985
+ * await storage.uploadFile({ file, filename: 'doc.pdf' });
986
+ * await storage.deleteFile({ fileId: '123' });
987
+ * ```
988
+ *
989
+ * @returns StorageServiceImpl with automatic error handling
990
+ */
991
+ getStorage() {
992
+ const self = this;
993
+ return new Proxy({}, {
994
+ get(_, prop) {
995
+ if (typeof prop === "symbol") {
996
+ return void 0;
997
+ }
998
+ const storage = self.getRawStorage();
999
+ const value = storage[prop];
1000
+ if (typeof value !== "function") {
1001
+ return value;
1002
+ }
1003
+ return (...args) => {
1004
+ try {
1005
+ const result = value.apply(storage, args);
1006
+ if (result instanceof Promise) {
1007
+ return result.catch((error) => {
1008
+ self.emitStorageError(error, prop, { recoverable: true });
1009
+ throw error;
1010
+ });
1011
+ }
1012
+ return result;
1013
+ } catch (error) {
1014
+ self.emitStorageError(error, prop, { recoverable: true });
1015
+ throw error;
1016
+ }
1017
+ };
1018
+ }
1019
+ });
1020
+ }
1021
+ // ─────────────────────────────────────────────────────────────────
1022
+ // Health Check (special handling for response transformation)
1023
+ // ─────────────────────────────────────────────────────────────────
1024
+ /**
1025
+ * Performs a health check on the storage service by checking all adapter health.
1026
+ * This method has special handling to transform the response format.
1027
+ */
1028
+ async healthCheck() {
1029
+ const startTime = Date.now();
1030
+ try {
1031
+ const storage = this.getRawStorage();
1032
+ if (typeof storage.checkAllAdaptersHealth === "function") {
1033
+ await storage.checkAllAdaptersHealth();
1034
+ }
1035
+ const summary = typeof storage.getHealthSummary === "function" ? storage.getHealthSummary() : null;
1036
+ const responseTime = Date.now() - startTime;
1037
+ return {
1038
+ // Check if all adapters are healthy (healthy count equals total count)
1039
+ isHealthy: summary ? summary.healthy === summary.total : true,
1040
+ responseTime,
1041
+ error: void 0
1042
+ };
1043
+ } catch (error) {
1044
+ this.emitStorageError(error, "healthCheck", { recoverable: true });
1045
+ return {
1046
+ isHealthy: false,
1047
+ responseTime: Date.now() - startTime,
1048
+ error: error instanceof Error ? error.message : "Unknown error"
1049
+ };
1050
+ }
1051
+ }
1052
+ // ─────────────────────────────────────────────────────────────────
1053
+ // Lifecycle
1054
+ // ─────────────────────────────────────────────────────────────────
1055
+ /**
1056
+ * Gets the current configuration
1057
+ */
1058
+ getConfig() {
1059
+ return this.config;
1060
+ }
1061
+ /**
1062
+ * Closes the storage service and cleans up resources
1063
+ */
1064
+ async close() {
1065
+ if (this.storageService) {
1066
+ await this.storageService.destroy();
1067
+ }
1068
+ this.storageService = null;
1069
+ this.initialized = false;
1070
+ this.config = null;
1071
+ }
1072
+ /**
1073
+ * Creates a dedicated storage service instance (NOT the singleton)
1074
+ *
1075
+ * Use this when you need an isolated storage connection with its own configuration.
1076
+ *
1077
+ * @param config - Storage service configuration
1078
+ * @returns Promise that resolves to a new dedicated StorageService instance
1079
+ */
1080
+ static async createInstance(config) {
1081
+ const dedicatedInstance = new _StorageService();
1082
+ dedicatedInstance.config = config;
1083
+ dedicatedInstance.storageService = new storage.StorageService(config);
1084
+ dedicatedInstance.initialized = true;
1085
+ return dedicatedInstance;
1086
+ }
797
1087
  };
798
- dedicatedInstance.config = mergedConfig;
799
- const dbConfig = dedicatedInstance.buildDatabaseConfig(mergedConfig);
800
- dedicatedInstance.databaseService = await db.createDatabaseService(dbConfig);
801
- if (mergedConfig.adapters) {
802
- for (const [name, adapterConfig] of Object.entries(mergedConfig.adapters)) {
803
- const namedDbConfig = dedicatedInstance.buildDatabaseConfig({
804
- ...mergedConfig,
805
- adapter: adapterConfig.adapter,
806
- drizzle: adapterConfig.drizzle,
807
- supabase: adapterConfig.supabase,
808
- sql: adapterConfig.sql
1088
+ }
1089
+ });
1090
+
1091
+ // src/services/NotificationService.ts
1092
+ var NotificationService_exports = {};
1093
+ __export(NotificationService_exports, {
1094
+ NotificationService: () => NotificationService
1095
+ });
1096
+ var NotificationService;
1097
+ var init_NotificationService = __esm({
1098
+ "src/services/NotificationService.ts"() {
1099
+ init_CoreEventManager();
1100
+ NotificationService = class _NotificationService {
1101
+ constructor() {
1102
+ this.notificationService = null;
1103
+ this.config = null;
1104
+ this.initialized = false;
1105
+ }
1106
+ static {
1107
+ __name(this, "NotificationService");
1108
+ }
1109
+ static {
1110
+ this.instance = null;
1111
+ }
1112
+ // ─────────────────────────────────────────────────────────────────
1113
+ // Error Handling
1114
+ // ─────────────────────────────────────────────────────────────────
1115
+ /**
1116
+ * Emits a notification error event via CoreEventManager.
1117
+ * Called when notification operations fail to integrate with global error handling.
1118
+ */
1119
+ emitNotificationError(error, operation, options) {
1120
+ const payload = {
1121
+ error,
1122
+ operation,
1123
+ recipientId: options?.recipientId,
1124
+ channel: options?.channel,
1125
+ recoverable: options?.recoverable ?? false
1126
+ };
1127
+ CoreEventManager.emit(core.CORE_EVENTS.NOTIFICATION.ERROR, payload);
1128
+ }
1129
+ // ─────────────────────────────────────────────────────────────────
1130
+ // Singleton Management
1131
+ // ─────────────────────────────────────────────────────────────────
1132
+ /**
1133
+ * Gets the singleton instance of NotificationService
1134
+ */
1135
+ static getInstance() {
1136
+ _NotificationService.instance ??= new _NotificationService();
1137
+ return _NotificationService.instance;
1138
+ }
1139
+ /**
1140
+ * Checks if the notification service has been initialized
1141
+ */
1142
+ static isInitialized() {
1143
+ return _NotificationService.instance?.initialized ?? false;
1144
+ }
1145
+ /**
1146
+ * Resets the notification service by clearing the singleton instance
1147
+ */
1148
+ static async reset() {
1149
+ if (_NotificationService.instance) {
1150
+ _NotificationService.instance.notificationService = null;
1151
+ _NotificationService.instance.config = null;
1152
+ _NotificationService.instance.initialized = false;
1153
+ _NotificationService.instance = null;
1154
+ }
1155
+ }
1156
+ /**
1157
+ * Initializes the notification service
1158
+ *
1159
+ * @param config - Notification service configuration
1160
+ * @returns The initialized NotificationService instance
1161
+ */
1162
+ static async initialize(config) {
1163
+ const instance = _NotificationService.getInstance();
1164
+ if (instance.initialized) {
1165
+ return instance;
1166
+ }
1167
+ instance.config = config;
1168
+ instance.notificationService = new notifications.NotificationService(config);
1169
+ instance.initialized = true;
1170
+ return instance;
1171
+ }
1172
+ /**
1173
+ * Gets the raw underlying notification service instance without error handling wrapper.
1174
+ * Use this only if you need direct access to the underlying service.
1175
+ *
1176
+ * @returns The raw NotificationService instance from @plyaz/notifications
1177
+ * @throws {NotificationsPackageError} When notifications is not initialized
1178
+ */
1179
+ getRawNotifications() {
1180
+ if (!this.notificationService) {
1181
+ throw new errors.NotificationPackageError(
1182
+ "Notifications not initialized. Call NotificationService.initialize() first or use Core.initialize() with notifications config.",
1183
+ errors$1.NOTIFICATION_ERROR_CODES.INITIALIZATION_FAILED
1184
+ );
1185
+ }
1186
+ return this.notificationService;
1187
+ }
1188
+ // ─────────────────────────────────────────────────────────────────
1189
+ // Service Access
1190
+ // ─────────────────────────────────────────────────────────────────
1191
+ /**
1192
+ * Gets the notification service with automatic error handling.
1193
+ * All method calls are wrapped with try/catch and emit error events on failure.
1194
+ * Any method added to @plyaz/notifications will be automatically available.
1195
+ *
1196
+ * @example
1197
+ * ```typescript
1198
+ * const notifications = NotificationService.getInstance().getNotifications();
1199
+ * await notifications.sendEmail({ to: 'user@example.com', templateId: 'welcome' });
1200
+ * await notifications.sendSMS({ to: '+1234567890', message: 'Hello!' });
1201
+ * ```
1202
+ *
1203
+ * @returns NotificationServiceImpl with automatic error handling
1204
+ */
1205
+ getNotifications() {
1206
+ const self = this;
1207
+ return new Proxy({}, {
1208
+ get(_, prop) {
1209
+ if (typeof prop === "symbol") {
1210
+ return void 0;
1211
+ }
1212
+ const notifications = self.getRawNotifications();
1213
+ const value = notifications[prop];
1214
+ if (typeof value !== "function") {
1215
+ return value;
1216
+ }
1217
+ return (...args) => {
1218
+ try {
1219
+ const result = value.apply(notifications, args);
1220
+ if (result instanceof Promise) {
1221
+ return result.catch((error) => {
1222
+ self.emitNotificationError(error, prop, { recoverable: true });
1223
+ throw error;
1224
+ });
1225
+ }
1226
+ return result;
1227
+ } catch (error) {
1228
+ self.emitNotificationError(error, prop, { recoverable: true });
1229
+ throw error;
1230
+ }
1231
+ };
1232
+ }
809
1233
  });
810
- const namedService = await db.createDatabaseService(namedDbConfig);
811
- dedicatedInstance.namedAdapters.set(name, namedService);
812
1234
  }
813
- }
814
- dedicatedInstance.initialized = true;
815
- return dedicatedInstance;
1235
+ // ─────────────────────────────────────────────────────────────────
1236
+ // Health Check (special handling for response transformation)
1237
+ // ─────────────────────────────────────────────────────────────────
1238
+ /**
1239
+ * Performs a health check on the notification service.
1240
+ * This method has special handling to transform the response format.
1241
+ */
1242
+ async healthCheck() {
1243
+ try {
1244
+ const notifications = this.getRawNotifications();
1245
+ const result = await notifications.healthCheck();
1246
+ return {
1247
+ isHealthy: result.healthy,
1248
+ providers: result.providers,
1249
+ // Use optional chaining since error may not exist on all health check results
1250
+ error: "error" in result ? result.error : void 0
1251
+ };
1252
+ } catch (error) {
1253
+ this.emitNotificationError(error, "healthCheck", { recoverable: true });
1254
+ return {
1255
+ isHealthy: false,
1256
+ error: error instanceof Error ? error.message : "Unknown error"
1257
+ };
1258
+ }
1259
+ }
1260
+ // ─────────────────────────────────────────────────────────────────
1261
+ // Lifecycle
1262
+ // ─────────────────────────────────────────────────────────────────
1263
+ /**
1264
+ * Gets the current configuration
1265
+ */
1266
+ getConfig() {
1267
+ return this.config;
1268
+ }
1269
+ /**
1270
+ * Closes the notification service and cleans up resources
1271
+ */
1272
+ async close() {
1273
+ this.notificationService = null;
1274
+ this.initialized = false;
1275
+ this.config = null;
1276
+ }
1277
+ /**
1278
+ * Creates a dedicated notification service instance (NOT the singleton)
1279
+ *
1280
+ * Use this when you need an isolated notification service with its own configuration.
1281
+ *
1282
+ * @param config - Notification service configuration
1283
+ * @returns Promise that resolves to a new dedicated NotificationService instance
1284
+ */
1285
+ static async createInstance(config) {
1286
+ const dedicatedInstance = new _NotificationService();
1287
+ dedicatedInstance.config = config;
1288
+ dedicatedInstance.notificationService = new notifications.NotificationService(config);
1289
+ dedicatedInstance.initialized = true;
1290
+ return dedicatedInstance;
1291
+ }
1292
+ };
816
1293
  }
817
- };
1294
+ });
1295
+
1296
+ // src/init/CoreInitializer.ts
1297
+ init_DbService();
1298
+
1299
+ // src/services/ApiClientService.ts
1300
+ init_CoreEventManager();
818
1301
  var MIN_RETRY_ATTEMPTS_PRODUCTION = 3;
819
1302
  function getConfigForEnvironment(env) {
820
1303
  switch (env) {
@@ -1900,566 +2383,174 @@ var CacheService = class _CacheService {
1900
2383
  /** Configuration used to initialize cache */
1901
2384
  this.config = null;
1902
2385
  /** Logger instance */
1903
- this.logger = new logger$1.CoreLogger({ service: "CacheService" });
1904
- }
1905
- static {
1906
- __name(this, "CacheService");
1907
- }
1908
- static {
1909
- /** Singleton instance */
1910
- this.instance = null;
1911
- }
1912
- /**
1913
- * Initialize the cache service with configuration
1914
- *
1915
- * @param config - Cache configuration
1916
- * @throws {CorePackageError} If already initialized or config is invalid
1917
- *
1918
- * @example
1919
- * ```typescript
1920
- * await CacheService.initialize({
1921
- * strategy: 'redis',
1922
- * isEnabled: true,
1923
- * ttl: 300,
1924
- * prefix: 'app',
1925
- * redis: { url: process.env.REDIS_URL },
1926
- * });
1927
- * ```
1928
- */
1929
- static async initialize(config$1) {
1930
- if (this.instance?.cacheManager) {
1931
- throw new errors.CorePackageError(
1932
- "CacheService is already initialized. Call reset() first if you need to reinitialize.",
1933
- errors$1.ERROR_CODES.VALIDATION_ERROR
1934
- );
1935
- }
1936
- const service = this.instance ?? new _CacheService();
1937
- this.instance = service;
1938
- try {
1939
- service.logger.info("[CacheService] Initializing cache...", {
1940
- strategy: config$1.strategy,
1941
- isEnabled: config$1.isEnabled,
1942
- ttl: config$1.ttl
1943
- });
1944
- service.validateConfig(config$1);
1945
- const cacheConfig = {
1946
- ...config$1,
1947
- ttl: config$1.ttl ?? config.TIME_CONSTANTS.DEFAULT_CACHE_TTL
1948
- };
1949
- service.cacheManager = new CacheManager(cacheConfig);
1950
- service.config = config$1;
1951
- service.logger.info("[CacheService] Cache initialized successfully", {
1952
- strategy: config$1.strategy
1953
- });
1954
- } catch (error) {
1955
- service.logger.error("[CacheService] Failed to initialize cache", { error });
1956
- throw new errors.CorePackageError(
1957
- `Failed to initialize cache: ${error instanceof Error ? error.message : String(error)}`,
1958
- errors$1.ERROR_CODES.VALIDATION_ERROR,
1959
- { cause: error instanceof Error ? error : void 0 }
1960
- );
1961
- }
1962
- }
1963
- /**
1964
- * Get the singleton instance
1965
- *
1966
- * @throws {CorePackageError} If not initialized
1967
- * @returns CacheService singleton instance
1968
- *
1969
- * @example
1970
- * ```typescript
1971
- * const cacheService = CacheService.getInstance();
1972
- * const cache = cacheService.getCacheManager();
1973
- * ```
1974
- */
1975
- static getInstance() {
1976
- if (!this.instance) {
1977
- throw new errors.CorePackageError(
1978
- "CacheService not initialized. Call CacheService.initialize() first.",
1979
- errors$1.ERROR_CODES.CLIENT_INITIALIZATION_FAILED
1980
- );
1981
- }
1982
- return this.instance;
1983
- }
1984
- /**
1985
- * Get the cache manager instance
1986
- *
1987
- * @throws {CorePackageError} If cache not initialized
1988
- * @returns CacheManager instance
1989
- *
1990
- * @example
1991
- * ```typescript
1992
- * const cache = CacheService.getInstance().getCacheManager();
1993
- * await cache.set('user:123', userData);
1994
- * const user = await cache.get('user:123');
1995
- * ```
1996
- */
1997
- getCacheManager() {
1998
- if (!this.cacheManager) {
1999
- throw new errors.CorePackageError(
2000
- "Cache manager not initialized. Call CacheService.initialize() first.",
2001
- errors$1.ERROR_CODES.CLIENT_INITIALIZATION_FAILED
2002
- );
2003
- }
2004
- return this.cacheManager;
2005
- }
2006
- /**
2007
- * Get current cache configuration
2008
- *
2009
- * @returns Current cache configuration or null if not initialized
2010
- */
2011
- getConfig() {
2012
- return this.config;
2013
- }
2014
- /**
2015
- * Check if cache is initialized and enabled
2016
- *
2017
- * @returns True if cache is ready to use
2018
- */
2019
- isInitialized() {
2020
- return this.cacheManager !== null && (this.config?.isEnabled ?? false);
2021
- }
2022
- /**
2023
- * Reset the cache service (useful for testing)
2024
- *
2025
- * @internal
2026
- */
2027
- static reset() {
2028
- if (this.instance?.cacheManager) {
2029
- this.instance.logger.info("[CacheService] Resetting cache service");
2030
- this.instance.cacheManager.clear().catch((error) => {
2031
- this.instance?.logger.error("[CacheService] Error clearing cache during reset", { error });
2032
- });
2033
- }
2034
- this.instance = null;
2035
- }
2036
- /**
2037
- * Validate cache configuration
2038
- *
2039
- * @param config - Configuration to validate
2040
- * @throws {CorePackageError} If configuration is invalid
2041
- * @private
2042
- */
2043
- // eslint-disable-next-line complexity
2044
- validateConfig(config) {
2045
- if (!features.CACHE_STRATEGIES.includes(config.strategy)) {
2046
- throw new errors.CorePackageError(
2047
- `Invalid cache strategy: ${config.strategy}. Must be one of: ${features.CACHE_STRATEGIES.join(", ")}`,
2048
- errors$1.ERROR_CODES.VALIDATION_ERROR
2049
- );
2050
- }
2051
- if (config.ttl !== void 0 && (config.ttl < 0 || !Number.isFinite(config.ttl))) {
2052
- throw new errors.CorePackageError(
2053
- `Invalid TTL value: ${config.ttl}. Must be a positive number.`,
2054
- errors$1.ERROR_CODES.VALIDATION_ERROR
2055
- );
2056
- }
2057
- if (config.strategy === "redis") {
2058
- if (!config.redis?.url && !config.redis?.host) {
2059
- throw new errors.CorePackageError(
2060
- "Redis strategy requires either redis.url or redis.host configuration",
2061
- errors$1.ERROR_CODES.VALIDATION_ERROR
2062
- );
2063
- }
2064
- }
2065
- this.logger.debug("[CacheService] Configuration validated successfully");
2066
- }
2067
- };
2068
- var StorageService = class _StorageService {
2069
- constructor() {
2070
- this.storageService = null;
2071
- this.config = null;
2072
- this.initialized = false;
2073
- }
2074
- static {
2075
- __name(this, "StorageService");
2076
- }
2077
- static {
2078
- this.instance = null;
2079
- }
2080
- // ─────────────────────────────────────────────────────────────────
2081
- // Error Handling
2082
- // ─────────────────────────────────────────────────────────────────
2083
- /**
2084
- * Emits a storage error event via CoreEventManager.
2085
- * Called when storage operations fail to integrate with global error handling.
2086
- */
2087
- emitStorageError(error, operation, options) {
2088
- const payload = {
2089
- error,
2090
- operation,
2091
- fileId: options?.fileId,
2092
- filename: options?.filename,
2093
- recoverable: options?.recoverable ?? false
2094
- };
2095
- CoreEventManager.emit(core.CORE_EVENTS.STORAGE.ERROR, payload);
2096
- }
2097
- // ─────────────────────────────────────────────────────────────────
2098
- // Singleton Management
2099
- // ─────────────────────────────────────────────────────────────────
2100
- /**
2101
- * Gets the singleton instance of StorageService
2102
- */
2103
- static getInstance() {
2104
- _StorageService.instance ??= new _StorageService();
2105
- return _StorageService.instance;
2106
- }
2107
- /**
2108
- * Checks if the storage service has been initialized
2109
- */
2110
- static isInitialized() {
2111
- return _StorageService.instance?.initialized ?? false;
2112
- }
2113
- /**
2114
- * Resets the storage service by clearing the singleton instance
2115
- */
2116
- static async reset() {
2117
- if (_StorageService.instance) {
2118
- _StorageService.instance.storageService = null;
2119
- _StorageService.instance.config = null;
2120
- _StorageService.instance.initialized = false;
2121
- _StorageService.instance = null;
2122
- }
2123
- }
2124
- /**
2125
- * Initializes the storage service
2126
- *
2127
- * @param config - Storage service configuration
2128
- * @returns The initialized StorageService instance
2129
- */
2130
- static async initialize(config) {
2131
- const instance = _StorageService.getInstance();
2132
- if (instance.initialized) {
2133
- return instance;
2134
- }
2135
- instance.config = config;
2136
- instance.storageService = new storage.StorageService(config);
2137
- instance.initialized = true;
2138
- return instance;
2139
- }
2140
- /**
2141
- * Gets the raw underlying storage service instance without error handling wrapper.
2142
- * Use this only if you need direct access to the underlying service.
2143
- *
2144
- * @returns The raw StorageService instance from @plyaz/storage
2145
- * @throws {StoragePackageError} When storage is not initialized
2146
- */
2147
- getRawStorage() {
2148
- if (!this.storageService) {
2149
- throw new errors.StoragePackageError(
2150
- "Storage not initialized. Call StorageService.initialize() first or use Core.initialize() with storage config.",
2151
- errors$1.STORAGE_ERROR_CODES.INITIALIZATION_FAILED
2152
- );
2153
- }
2154
- return this.storageService;
2155
- }
2156
- // ─────────────────────────────────────────────────────────────────
2157
- // Service Access
2158
- // ─────────────────────────────────────────────────────────────────
2159
- /**
2160
- * Gets the storage service with automatic error handling.
2161
- * All method calls are wrapped with try/catch and emit error events on failure.
2162
- * Any method added to @plyaz/storage will be automatically available.
2163
- *
2164
- * @example
2165
- * ```typescript
2166
- * const storage = StorageService.getInstance().getStorage();
2167
- * await storage.uploadFile({ file, filename: 'doc.pdf' });
2168
- * await storage.deleteFile({ fileId: '123' });
2169
- * ```
2170
- *
2171
- * @returns StorageServiceImpl with automatic error handling
2172
- */
2173
- getStorage() {
2174
- const self = this;
2175
- return new Proxy({}, {
2176
- get(_, prop) {
2177
- if (typeof prop === "symbol") {
2178
- return void 0;
2179
- }
2180
- const storage = self.getRawStorage();
2181
- const value = storage[prop];
2182
- if (typeof value !== "function") {
2183
- return value;
2184
- }
2185
- return (...args) => {
2186
- try {
2187
- const result = value.apply(storage, args);
2188
- if (result instanceof Promise) {
2189
- return result.catch((error) => {
2190
- self.emitStorageError(error, prop, { recoverable: true });
2191
- throw error;
2192
- });
2193
- }
2194
- return result;
2195
- } catch (error) {
2196
- self.emitStorageError(error, prop, { recoverable: true });
2197
- throw error;
2198
- }
2199
- };
2200
- }
2201
- });
2202
- }
2203
- // ─────────────────────────────────────────────────────────────────
2204
- // Health Check (special handling for response transformation)
2205
- // ─────────────────────────────────────────────────────────────────
2206
- /**
2207
- * Performs a health check on the storage service by checking all adapter health.
2208
- * This method has special handling to transform the response format.
2209
- */
2210
- async healthCheck() {
2211
- const startTime = Date.now();
2212
- try {
2213
- const storage = this.getRawStorage();
2214
- if (typeof storage.checkAllAdaptersHealth === "function") {
2215
- await storage.checkAllAdaptersHealth();
2216
- }
2217
- const summary = typeof storage.getHealthSummary === "function" ? storage.getHealthSummary() : null;
2218
- const responseTime = Date.now() - startTime;
2219
- return {
2220
- // Check if all adapters are healthy (healthy count equals total count)
2221
- isHealthy: summary ? summary.healthy === summary.total : true,
2222
- responseTime,
2223
- error: void 0
2224
- };
2225
- } catch (error) {
2226
- this.emitStorageError(error, "healthCheck", { recoverable: true });
2227
- return {
2228
- isHealthy: false,
2229
- responseTime: Date.now() - startTime,
2230
- error: error instanceof Error ? error.message : "Unknown error"
2231
- };
2232
- }
2233
- }
2234
- // ─────────────────────────────────────────────────────────────────
2235
- // Lifecycle
2236
- // ─────────────────────────────────────────────────────────────────
2237
- /**
2238
- * Gets the current configuration
2239
- */
2240
- getConfig() {
2241
- return this.config;
2242
- }
2243
- /**
2244
- * Closes the storage service and cleans up resources
2245
- */
2246
- async close() {
2247
- if (this.storageService) {
2248
- await this.storageService.destroy();
2249
- }
2250
- this.storageService = null;
2251
- this.initialized = false;
2252
- this.config = null;
2253
- }
2254
- /**
2255
- * Creates a dedicated storage service instance (NOT the singleton)
2256
- *
2257
- * Use this when you need an isolated storage connection with its own configuration.
2258
- *
2259
- * @param config - Storage service configuration
2260
- * @returns Promise that resolves to a new dedicated StorageService instance
2261
- */
2262
- static async createInstance(config) {
2263
- const dedicatedInstance = new _StorageService();
2264
- dedicatedInstance.config = config;
2265
- dedicatedInstance.storageService = new storage.StorageService(config);
2266
- dedicatedInstance.initialized = true;
2267
- return dedicatedInstance;
2268
- }
2269
- };
2270
- var NotificationService = class _NotificationService {
2271
- constructor() {
2272
- this.notificationService = null;
2273
- this.config = null;
2274
- this.initialized = false;
2386
+ this.logger = new logger$1.CoreLogger({ service: "CacheService" });
2275
2387
  }
2276
2388
  static {
2277
- __name(this, "NotificationService");
2389
+ __name(this, "CacheService");
2278
2390
  }
2279
2391
  static {
2392
+ /** Singleton instance */
2280
2393
  this.instance = null;
2281
2394
  }
2282
- // ─────────────────────────────────────────────────────────────────
2283
- // Error Handling
2284
- // ─────────────────────────────────────────────────────────────────
2285
- /**
2286
- * Emits a notification error event via CoreEventManager.
2287
- * Called when notification operations fail to integrate with global error handling.
2288
- */
2289
- emitNotificationError(error, operation, options) {
2290
- const payload = {
2291
- error,
2292
- operation,
2293
- recipientId: options?.recipientId,
2294
- channel: options?.channel,
2295
- recoverable: options?.recoverable ?? false
2296
- };
2297
- CoreEventManager.emit(core.CORE_EVENTS.NOTIFICATION.ERROR, payload);
2298
- }
2299
- // ─────────────────────────────────────────────────────────────────
2300
- // Singleton Management
2301
- // ─────────────────────────────────────────────────────────────────
2302
- /**
2303
- * Gets the singleton instance of NotificationService
2304
- */
2305
- static getInstance() {
2306
- _NotificationService.instance ??= new _NotificationService();
2307
- return _NotificationService.instance;
2308
- }
2309
- /**
2310
- * Checks if the notification service has been initialized
2311
- */
2312
- static isInitialized() {
2313
- return _NotificationService.instance?.initialized ?? false;
2314
- }
2315
- /**
2316
- * Resets the notification service by clearing the singleton instance
2317
- */
2318
- static async reset() {
2319
- if (_NotificationService.instance) {
2320
- _NotificationService.instance.notificationService = null;
2321
- _NotificationService.instance.config = null;
2322
- _NotificationService.instance.initialized = false;
2323
- _NotificationService.instance = null;
2324
- }
2325
- }
2326
2395
  /**
2327
- * Initializes the notification service
2396
+ * Initialize the cache service with configuration
2328
2397
  *
2329
- * @param config - Notification service configuration
2330
- * @returns The initialized NotificationService instance
2398
+ * @param config - Cache configuration
2399
+ * @throws {CorePackageError} If already initialized or config is invalid
2400
+ *
2401
+ * @example
2402
+ * ```typescript
2403
+ * await CacheService.initialize({
2404
+ * strategy: 'redis',
2405
+ * isEnabled: true,
2406
+ * ttl: 300,
2407
+ * prefix: 'app',
2408
+ * redis: { url: process.env.REDIS_URL },
2409
+ * });
2410
+ * ```
2331
2411
  */
2332
- static async initialize(config) {
2333
- const instance = _NotificationService.getInstance();
2334
- if (instance.initialized) {
2335
- return instance;
2412
+ static async initialize(config$1) {
2413
+ if (this.instance?.cacheManager) {
2414
+ throw new errors.CorePackageError(
2415
+ "CacheService is already initialized. Call reset() first if you need to reinitialize.",
2416
+ errors$1.ERROR_CODES.VALIDATION_ERROR
2417
+ );
2418
+ }
2419
+ const service = this.instance ?? new _CacheService();
2420
+ this.instance = service;
2421
+ try {
2422
+ service.logger.info("[CacheService] Initializing cache...", {
2423
+ strategy: config$1.strategy,
2424
+ isEnabled: config$1.isEnabled,
2425
+ ttl: config$1.ttl
2426
+ });
2427
+ service.validateConfig(config$1);
2428
+ const cacheConfig = {
2429
+ ...config$1,
2430
+ ttl: config$1.ttl ?? config.TIME_CONSTANTS.DEFAULT_CACHE_TTL
2431
+ };
2432
+ service.cacheManager = new CacheManager(cacheConfig);
2433
+ service.config = config$1;
2434
+ service.logger.info("[CacheService] Cache initialized successfully", {
2435
+ strategy: config$1.strategy
2436
+ });
2437
+ } catch (error) {
2438
+ service.logger.error("[CacheService] Failed to initialize cache", { error });
2439
+ throw new errors.CorePackageError(
2440
+ `Failed to initialize cache: ${error instanceof Error ? error.message : String(error)}`,
2441
+ errors$1.ERROR_CODES.VALIDATION_ERROR,
2442
+ { cause: error instanceof Error ? error : void 0 }
2443
+ );
2336
2444
  }
2337
- instance.config = config;
2338
- instance.notificationService = new notifications.NotificationService(config);
2339
- instance.initialized = true;
2340
- return instance;
2341
2445
  }
2342
2446
  /**
2343
- * Gets the raw underlying notification service instance without error handling wrapper.
2344
- * Use this only if you need direct access to the underlying service.
2447
+ * Get the singleton instance
2345
2448
  *
2346
- * @returns The raw NotificationService instance from @plyaz/notifications
2347
- * @throws {NotificationsPackageError} When notifications is not initialized
2449
+ * @throws {CorePackageError} If not initialized
2450
+ * @returns CacheService singleton instance
2451
+ *
2452
+ * @example
2453
+ * ```typescript
2454
+ * const cacheService = CacheService.getInstance();
2455
+ * const cache = cacheService.getCacheManager();
2456
+ * ```
2348
2457
  */
2349
- getRawNotifications() {
2350
- if (!this.notificationService) {
2351
- throw new errors.NotificationPackageError(
2352
- "Notifications not initialized. Call NotificationService.initialize() first or use Core.initialize() with notifications config.",
2353
- errors$1.NOTIFICATION_ERROR_CODES.INITIALIZATION_FAILED
2458
+ static getInstance() {
2459
+ if (!this.instance) {
2460
+ throw new errors.CorePackageError(
2461
+ "CacheService not initialized. Call CacheService.initialize() first.",
2462
+ errors$1.ERROR_CODES.CLIENT_INITIALIZATION_FAILED
2354
2463
  );
2355
2464
  }
2356
- return this.notificationService;
2465
+ return this.instance;
2357
2466
  }
2358
- // ─────────────────────────────────────────────────────────────────
2359
- // Service Access
2360
- // ─────────────────────────────────────────────────────────────────
2361
2467
  /**
2362
- * Gets the notification service with automatic error handling.
2363
- * All method calls are wrapped with try/catch and emit error events on failure.
2364
- * Any method added to @plyaz/notifications will be automatically available.
2468
+ * Get the cache manager instance
2469
+ *
2470
+ * @throws {CorePackageError} If cache not initialized
2471
+ * @returns CacheManager instance
2365
2472
  *
2366
2473
  * @example
2367
2474
  * ```typescript
2368
- * const notifications = NotificationService.getInstance().getNotifications();
2369
- * await notifications.sendEmail({ to: 'user@example.com', templateId: 'welcome' });
2370
- * await notifications.sendSMS({ to: '+1234567890', message: 'Hello!' });
2475
+ * const cache = CacheService.getInstance().getCacheManager();
2476
+ * await cache.set('user:123', userData);
2477
+ * const user = await cache.get('user:123');
2371
2478
  * ```
2372
- *
2373
- * @returns NotificationServiceImpl with automatic error handling
2374
- */
2375
- getNotifications() {
2376
- const self = this;
2377
- return new Proxy({}, {
2378
- get(_, prop) {
2379
- if (typeof prop === "symbol") {
2380
- return void 0;
2381
- }
2382
- const notifications = self.getRawNotifications();
2383
- const value = notifications[prop];
2384
- if (typeof value !== "function") {
2385
- return value;
2386
- }
2387
- return (...args) => {
2388
- try {
2389
- const result = value.apply(notifications, args);
2390
- if (result instanceof Promise) {
2391
- return result.catch((error) => {
2392
- self.emitNotificationError(error, prop, { recoverable: true });
2393
- throw error;
2394
- });
2395
- }
2396
- return result;
2397
- } catch (error) {
2398
- self.emitNotificationError(error, prop, { recoverable: true });
2399
- throw error;
2400
- }
2401
- };
2402
- }
2403
- });
2404
- }
2405
- // ─────────────────────────────────────────────────────────────────
2406
- // Health Check (special handling for response transformation)
2407
- // ─────────────────────────────────────────────────────────────────
2408
- /**
2409
- * Performs a health check on the notification service.
2410
- * This method has special handling to transform the response format.
2411
2479
  */
2412
- async healthCheck() {
2413
- try {
2414
- const notifications = this.getRawNotifications();
2415
- const result = await notifications.healthCheck();
2416
- return {
2417
- isHealthy: result.healthy,
2418
- providers: result.providers,
2419
- // Use optional chaining since error may not exist on all health check results
2420
- error: "error" in result ? result.error : void 0
2421
- };
2422
- } catch (error) {
2423
- this.emitNotificationError(error, "healthCheck", { recoverable: true });
2424
- return {
2425
- isHealthy: false,
2426
- error: error instanceof Error ? error.message : "Unknown error"
2427
- };
2480
+ getCacheManager() {
2481
+ if (!this.cacheManager) {
2482
+ throw new errors.CorePackageError(
2483
+ "Cache manager not initialized. Call CacheService.initialize() first.",
2484
+ errors$1.ERROR_CODES.CLIENT_INITIALIZATION_FAILED
2485
+ );
2428
2486
  }
2487
+ return this.cacheManager;
2429
2488
  }
2430
- // ─────────────────────────────────────────────────────────────────
2431
- // Lifecycle
2432
- // ─────────────────────────────────────────────────────────────────
2433
2489
  /**
2434
- * Gets the current configuration
2490
+ * Get current cache configuration
2491
+ *
2492
+ * @returns Current cache configuration or null if not initialized
2435
2493
  */
2436
2494
  getConfig() {
2437
2495
  return this.config;
2438
2496
  }
2439
2497
  /**
2440
- * Closes the notification service and cleans up resources
2498
+ * Check if cache is initialized and enabled
2499
+ *
2500
+ * @returns True if cache is ready to use
2441
2501
  */
2442
- async close() {
2443
- this.notificationService = null;
2444
- this.initialized = false;
2445
- this.config = null;
2502
+ isInitialized() {
2503
+ return this.cacheManager !== null && (this.config?.isEnabled ?? false);
2446
2504
  }
2447
2505
  /**
2448
- * Creates a dedicated notification service instance (NOT the singleton)
2506
+ * Reset the cache service (useful for testing)
2449
2507
  *
2450
- * Use this when you need an isolated notification service with its own configuration.
2508
+ * @internal
2509
+ */
2510
+ static reset() {
2511
+ if (this.instance?.cacheManager) {
2512
+ this.instance.logger.info("[CacheService] Resetting cache service");
2513
+ this.instance.cacheManager.clear().catch((error) => {
2514
+ this.instance?.logger.error("[CacheService] Error clearing cache during reset", { error });
2515
+ });
2516
+ }
2517
+ this.instance = null;
2518
+ }
2519
+ /**
2520
+ * Validate cache configuration
2451
2521
  *
2452
- * @param config - Notification service configuration
2453
- * @returns Promise that resolves to a new dedicated NotificationService instance
2522
+ * @param config - Configuration to validate
2523
+ * @throws {CorePackageError} If configuration is invalid
2524
+ * @private
2454
2525
  */
2455
- static async createInstance(config) {
2456
- const dedicatedInstance = new _NotificationService();
2457
- dedicatedInstance.config = config;
2458
- dedicatedInstance.notificationService = new notifications.NotificationService(config);
2459
- dedicatedInstance.initialized = true;
2460
- return dedicatedInstance;
2526
+ // eslint-disable-next-line complexity
2527
+ validateConfig(config) {
2528
+ if (!features.CACHE_STRATEGIES.includes(config.strategy)) {
2529
+ throw new errors.CorePackageError(
2530
+ `Invalid cache strategy: ${config.strategy}. Must be one of: ${features.CACHE_STRATEGIES.join(", ")}`,
2531
+ errors$1.ERROR_CODES.VALIDATION_ERROR
2532
+ );
2533
+ }
2534
+ if (config.ttl !== void 0 && (config.ttl < 0 || !Number.isFinite(config.ttl))) {
2535
+ throw new errors.CorePackageError(
2536
+ `Invalid TTL value: ${config.ttl}. Must be a positive number.`,
2537
+ errors$1.ERROR_CODES.VALIDATION_ERROR
2538
+ );
2539
+ }
2540
+ if (config.strategy === "redis") {
2541
+ if (!config.redis?.url && !config.redis?.host) {
2542
+ throw new errors.CorePackageError(
2543
+ "Redis strategy requires either redis.url or redis.host configuration",
2544
+ errors$1.ERROR_CODES.VALIDATION_ERROR
2545
+ );
2546
+ }
2547
+ }
2548
+ this.logger.debug("[CacheService] Configuration validated successfully");
2461
2549
  }
2462
2550
  };
2551
+
2552
+ // src/init/CoreInitializer.ts
2553
+ init_CoreEventManager();
2463
2554
  var BaseDomainService = class {
2464
2555
  // ─────────────────────────────────────────────────────────────────────────
2465
2556
  // Constructor
@@ -2858,6 +2949,9 @@ var BaseDomainService = class {
2858
2949
  );
2859
2950
  }
2860
2951
  };
2952
+
2953
+ // src/domain/base/BaseFrontendDomainService.ts
2954
+ init_CoreEventManager();
2861
2955
  var BaseFrontendDomainService = class extends BaseDomainService {
2862
2956
  // ─────────────────────────────────────────────────────────────────────────
2863
2957
  // Constructor
@@ -3755,6 +3849,9 @@ var BaseFrontendDomainService = class extends BaseDomainService {
3755
3849
  );
3756
3850
  }
3757
3851
  };
3852
+
3853
+ // src/events/index.ts
3854
+ init_CoreEventManager();
3758
3855
  var BaseBackendDomainService = class extends BaseDomainService {
3759
3856
  static {
3760
3857
  __name(this, "BaseBackendDomainService");
@@ -5108,6 +5205,10 @@ function trimString(str) {
5108
5205
  return str?.trim();
5109
5206
  }
5110
5207
  __name(trimString, "trimString");
5208
+
5209
+ // src/models/example/ExampleRepository.ts
5210
+ init_DbService();
5211
+ init_common();
5111
5212
  var EXAMPLE_TABLE = "examples";
5112
5213
  var DEFAULT_PAGINATION_LIMIT = 100;
5113
5214
  var DUMMY_DATA = [
@@ -5285,31 +5386,99 @@ var ExampleRepository = class _ExampleRepository extends db.BaseRepository {
5285
5386
  return super.delete(id, config);
5286
5387
  }
5287
5388
  // ─────────────────────────────────────────────────────────────────────────
5288
- // Domain-Specific Query Methods
5389
+ // Domain-Specific Query Methods (using QueryBuilder fluent API)
5289
5390
  // ─────────────────────────────────────────────────────────────────────────
5290
5391
  /**
5291
- * Find entities by status
5392
+ * Find entities by status using fluent QueryBuilder
5393
+ *
5394
+ * @example
5395
+ * ```typescript
5396
+ * const result = await repo.findByStatus('active');
5397
+ * ```
5292
5398
  */
5293
5399
  async findByStatus(status) {
5294
- return this.findMany({
5295
- filter: { field: "status", operator: "eq", value: status }
5296
- });
5400
+ if (this.useDummyData) {
5401
+ return this.findMany({
5402
+ filter: { field: "status", operator: "eq", value: status }
5403
+ });
5404
+ }
5405
+ return this.query().where("status", "eq", status).execute();
5297
5406
  }
5298
5407
  /**
5299
- * Find entities by owner
5408
+ * Find entities by owner using fluent QueryBuilder
5409
+ *
5410
+ * @example
5411
+ * ```typescript
5412
+ * const result = await repo.findByOwner('user-001');
5413
+ * ```
5300
5414
  */
5301
5415
  async findByOwner(ownerId) {
5302
- return this.findMany({
5303
- filter: { field: "owner_id", operator: "eq", value: ownerId }
5304
- });
5416
+ if (this.useDummyData) {
5417
+ return this.findMany({
5418
+ filter: { field: "owner_id", operator: "eq", value: ownerId }
5419
+ });
5420
+ }
5421
+ return this.query().where("owner_id", "eq", ownerId).execute();
5305
5422
  }
5306
5423
  /**
5307
- * Find visible entities only
5424
+ * Find visible entities only using fluent QueryBuilder
5425
+ *
5426
+ * @example
5427
+ * ```typescript
5428
+ * const result = await repo.findVisible();
5429
+ * ```
5308
5430
  */
5309
5431
  async findVisible() {
5310
- return this.findMany({
5311
- filter: { field: "is_visible", operator: "eq", value: true }
5312
- });
5432
+ if (this.useDummyData) {
5433
+ return this.findMany({
5434
+ filter: { field: "is_visible", operator: "eq", value: true }
5435
+ });
5436
+ }
5437
+ return this.query().where("is_visible", "eq", true).execute();
5438
+ }
5439
+ /**
5440
+ * Find active and visible entities using complex QueryBuilder
5441
+ *
5442
+ * @example
5443
+ * ```typescript
5444
+ * const items = await repo.findActiveAndVisible();
5445
+ * ```
5446
+ */
5447
+ async findActiveAndVisible() {
5448
+ if (this.useDummyData) {
5449
+ return this.dummyStore.filter((r) => r.status === "active" && r.is_visible && !r.deleted_at);
5450
+ }
5451
+ return this.query().where("status", "eq", "active").andWhere("is_visible", "eq", true).whereNull("deleted_at").orderByDesc("created_at").getMany();
5452
+ }
5453
+ /**
5454
+ * Find entities by owner with status filter using QueryBuilder
5455
+ *
5456
+ * @example
5457
+ * ```typescript
5458
+ * const items = await repo.findByOwnerWithStatus('user-001', 'active');
5459
+ * ```
5460
+ */
5461
+ async findByOwnerWithStatus(ownerId, status) {
5462
+ if (this.useDummyData) {
5463
+ return this.dummyStore.filter(
5464
+ (r) => r.owner_id === ownerId && r.status === status && !r.deleted_at
5465
+ );
5466
+ }
5467
+ return this.query().where("owner_id", "eq", ownerId).andWhere("status", "eq", status).orderByDesc("updated_at").getMany();
5468
+ }
5469
+ /**
5470
+ * Count entities by status using QueryBuilder
5471
+ *
5472
+ * @example
5473
+ * ```typescript
5474
+ * const count = await repo.countByStatus('active');
5475
+ * ```
5476
+ */
5477
+ async countByStatus(status) {
5478
+ if (this.useDummyData) {
5479
+ return this.dummyStore.filter((r) => r.status === status && !r.deleted_at).length;
5480
+ }
5481
+ return this.query().where("status", "eq", status).count();
5313
5482
  }
5314
5483
  /**
5315
5484
  * Check if entity exists by ID
@@ -5780,6 +5949,7 @@ var BackendExampleDomainService = class _BackendExampleDomainService extends Bas
5780
5949
  // ─────────────────────────────────────────────────────────────────────────
5781
5950
  };
5782
5951
  new BackendExampleDomainService();
5952
+ init_CoreEventManager();
5783
5953
  var DEFAULT_POLLING_INTERVAL_MS = 3e4;
5784
5954
  var FrontendExampleDomainService = class _FrontendExampleDomainService extends BaseFrontendDomainService {
5785
5955
  // ─────────────────────────────────────────────────────────────────────────
@@ -5829,6 +5999,7 @@ var FrontendExampleDomainService = class _FrontendExampleDomainService extends B
5829
5999
  // store.setData(data);
5830
6000
  // // Can also call domain-specific methods or other stores
5831
6001
  // // store.selectItem(data.selectedId);
6002
+ // // store.selectItem(data.selectedId);
5832
6003
  // },
5833
6004
  // updateData: (store, data) => store.updateData(data),
5834
6005
  // setLoading: (store, isLoading) => store.setLoading(isLoading),
@@ -6044,6 +6215,9 @@ var FrontendExampleDomainService = class _FrontendExampleDomainService extends B
6044
6215
  // All events are emitted automatically with 'example:' prefix!
6045
6216
  // ─────────────────────────────────────────────────────────────────────────
6046
6217
  };
6218
+
6219
+ // src/base/observability/BaseAdapter.ts
6220
+ init_common();
6047
6221
  var BaseAdapter = class {
6048
6222
  constructor() {
6049
6223
  this._isInitialized = false;
@@ -6266,6 +6440,9 @@ var NoopAdapter = class extends BaseAdapter {
6266
6440
  async doFlush() {
6267
6441
  }
6268
6442
  };
6443
+
6444
+ // src/base/observability/LoggerAdapter.ts
6445
+ init_common();
6269
6446
  var LoggerAdapter = class extends BaseAdapter {
6270
6447
  constructor() {
6271
6448
  super(...arguments);
@@ -6415,6 +6592,12 @@ var LoggerAdapter = class extends BaseAdapter {
6415
6592
  async doFlush() {
6416
6593
  }
6417
6594
  };
6595
+
6596
+ // src/base/observability/CompositeAdapter.ts
6597
+ init_common();
6598
+
6599
+ // src/base/observability/DatadogAdapter.ts
6600
+ init_common();
6418
6601
  var DEFAULT_ADAPTER_TIMEOUT_MS = 5e3;
6419
6602
  var ObservabilityService = class {
6420
6603
  constructor() {
@@ -7167,10 +7350,11 @@ var ServiceRegistry = class _ServiceRegistry {
7167
7350
  if (!dbConfig) {
7168
7351
  return void 0;
7169
7352
  }
7353
+ const { DbService: DbService2 } = await Promise.resolve().then(() => (init_DbService(), DbService_exports));
7170
7354
  let dbInstance;
7171
7355
  if (isDedicated && dbConfig) {
7172
7356
  try {
7173
- dbInstance = await DbService.createInstance(dbConfig);
7357
+ dbInstance = await DbService2.createInstance(dbConfig);
7174
7358
  _ServiceRegistry.logger.debug(
7175
7359
  `Created dedicated database instance for service '${serviceKey}'`
7176
7360
  );
@@ -7184,7 +7368,7 @@ var ServiceRegistry = class _ServiceRegistry {
7184
7368
  }
7185
7369
  } else {
7186
7370
  try {
7187
- dbInstance = DbService.getInstance();
7371
+ dbInstance = DbService2.getInstance();
7188
7372
  } catch {
7189
7373
  _ServiceRegistry.logger.debug(
7190
7374
  `Global DB not initialized, service '${serviceKey}' will not have DB`
@@ -7285,10 +7469,11 @@ var ServiceRegistry = class _ServiceRegistry {
7285
7469
  if (!storageConfig) {
7286
7470
  return void 0;
7287
7471
  }
7472
+ const { StorageService: StorageService2 } = await Promise.resolve().then(() => (init_StorageService(), StorageService_exports));
7288
7473
  let storageInstance;
7289
7474
  if (isDedicated && storageConfig) {
7290
7475
  try {
7291
- storageInstance = await StorageService.createInstance(storageConfig);
7476
+ storageInstance = await StorageService2.createInstance(storageConfig);
7292
7477
  _ServiceRegistry.logger.debug(
7293
7478
  `Created dedicated storage instance for service '${serviceKey}'`
7294
7479
  );
@@ -7300,7 +7485,7 @@ var ServiceRegistry = class _ServiceRegistry {
7300
7485
  }
7301
7486
  } else {
7302
7487
  try {
7303
- storageInstance = StorageService.getInstance();
7488
+ storageInstance = StorageService2.getInstance();
7304
7489
  } catch {
7305
7490
  _ServiceRegistry.logger.debug(
7306
7491
  `Global storage not initialized, service '${serviceKey}' will not have storage`
@@ -7330,10 +7515,11 @@ var ServiceRegistry = class _ServiceRegistry {
7330
7515
  if (!notificationsConfig) {
7331
7516
  return void 0;
7332
7517
  }
7518
+ const { NotificationService: NotificationService2 } = await Promise.resolve().then(() => (init_NotificationService(), NotificationService_exports));
7333
7519
  let notificationsInstance;
7334
7520
  if (isDedicated && notificationsConfig) {
7335
7521
  try {
7336
- notificationsInstance = await NotificationService.createInstance(notificationsConfig);
7522
+ notificationsInstance = await NotificationService2.createInstance(notificationsConfig);
7337
7523
  _ServiceRegistry.logger.debug(
7338
7524
  `Created dedicated notifications instance for service '${serviceKey}'`
7339
7525
  );
@@ -7345,7 +7531,7 @@ var ServiceRegistry = class _ServiceRegistry {
7345
7531
  }
7346
7532
  } else {
7347
7533
  try {
7348
- notificationsInstance = NotificationService.getInstance();
7534
+ notificationsInstance = NotificationService2.getInstance();
7349
7535
  } catch {
7350
7536
  _ServiceRegistry.logger.debug(
7351
7537
  `Global notifications not initialized, service '${serviceKey}' will not have notifications`
@@ -7923,8 +8109,9 @@ var Core = class _Core {
7923
8109
  await _Core.initService(
7924
8110
  "storage",
7925
8111
  async () => {
7926
- await StorageService.initialize(storageConfig);
7927
- _Core._coreServices.storage = StorageService.getInstance();
8112
+ const { StorageService: StorageService2 } = await Promise.resolve().then(() => (init_StorageService(), StorageService_exports));
8113
+ await StorageService2.initialize(storageConfig);
8114
+ _Core._coreServices.storage = StorageService2.getInstance();
7928
8115
  _Core.log("Storage service initialized", verbose);
7929
8116
  },
7930
8117
  verbose
@@ -7948,8 +8135,9 @@ var Core = class _Core {
7948
8135
  await _Core.initService(
7949
8136
  "notifications",
7950
8137
  async () => {
7951
- await NotificationService.initialize(notificationsConfig);
7952
- _Core._coreServices.notifications = NotificationService.getInstance();
8138
+ const { NotificationService: NotificationService2 } = await Promise.resolve().then(() => (init_NotificationService(), NotificationService_exports));
8139
+ await NotificationService2.initialize(notificationsConfig);
8140
+ _Core._coreServices.notifications = NotificationService2.getInstance();
7953
8141
  _Core.log("Notifications service initialized", verbose);
7954
8142
  },
7955
8143
  verbose
@@ -8514,11 +8702,13 @@ var Core = class _Core {
8514
8702
  _Core._observabilityConfig = {};
8515
8703
  if (_Core._coreServices.storage) {
8516
8704
  await _Core._coreServices.storage.close();
8517
- await StorageService.reset();
8705
+ const { StorageService: StorageService2 } = await Promise.resolve().then(() => (init_StorageService(), StorageService_exports));
8706
+ await StorageService2.reset();
8518
8707
  }
8519
8708
  if (_Core._coreServices.notifications) {
8520
8709
  await _Core._coreServices.notifications.close();
8521
- await NotificationService.reset();
8710
+ const { NotificationService: NotificationService2 } = await Promise.resolve().then(() => (init_NotificationService(), NotificationService_exports));
8711
+ await NotificationService2.reset();
8522
8712
  }
8523
8713
  if (_Core._errorHandler) {
8524
8714
  _Core._errorHandler.destroy();
@@ -8569,20 +8759,19 @@ var Core = class _Core {
8569
8759
  */
8570
8760
  static shouldSkipDbService(skipDb, verbose) {
8571
8761
  if (skipDb === true) {
8572
- if (verbose) {
8573
- console.log("[Core] Database service skipped (skipDb: true)");
8574
- }
8762
+ _Core.log("Database service skipped (skipDb: true)", verbose);
8575
8763
  return true;
8576
8764
  }
8577
8765
  const isFrontend = _Core.isFrontend;
8578
8766
  if (isFrontend) {
8579
8767
  if (skipDb === false) {
8580
- console.warn(
8581
- "[Core] Warning: DbService cannot be initialized on frontend runtime. DbService is backend-only and requires Node.js. Use API calls from frontend instead. Skipping database initialization."
8768
+ _Core.logger.warn(
8769
+ "DbService cannot be initialized on frontend runtime. DbService is backend-only and requires Node.js. Use API calls from frontend instead. Skipping database initialization."
8582
8770
  );
8583
8771
  } else if (verbose) {
8584
- console.log(
8585
- "[Core] Database service skipped (frontend runtime detected). DbService is only available on backend."
8772
+ _Core.log(
8773
+ "Database service skipped (frontend runtime detected). DbService is only available on backend.",
8774
+ verbose
8586
8775
  );
8587
8776
  }
8588
8777
  return true;
@@ -8594,10 +8783,9 @@ var Core = class _Core {
8594
8783
  * Config validation is handled by DbService
8595
8784
  */
8596
8785
  static async initializeDb(config, verbose) {
8597
- if (verbose) {
8598
- console.log(`[Core] Initializing database with adapter: ${config?.adapter ?? "drizzle"}`);
8599
- }
8600
- return DbService.initialize({
8786
+ _Core.log(`Initializing database with adapter: ${config?.adapter ?? "drizzle"}`, verbose);
8787
+ const { DbService: DbService2 } = await Promise.resolve().then(() => (init_DbService(), DbService_exports));
8788
+ return DbService2.initialize({
8601
8789
  adapter: "drizzle",
8602
8790
  ...config
8603
8791
  });
@@ -8609,9 +8797,7 @@ var Core = class _Core {
8609
8797
  static async initializeApi(config, globalEnvironment, verbose) {
8610
8798
  const { env: apiEnv, setAsDefault, ...apiClientOptions } = config ?? {};
8611
8799
  const resolvedEnv = apiEnv ?? globalEnvironment ?? "development";
8612
- if (verbose) {
8613
- console.log(`[Core] Initializing API client for environment: ${resolvedEnv}`);
8614
- }
8800
+ _Core.log(`Initializing API client for environment: ${resolvedEnv}`, verbose);
8615
8801
  await ApiClientService.init(
8616
8802
  {
8617
8803
  env: resolvedEnv,
@@ -8625,9 +8811,7 @@ var Core = class _Core {
8625
8811
  * Config validation is handled by CacheService
8626
8812
  */
8627
8813
  static async initializeCache(config, verbose) {
8628
- if (verbose) {
8629
- console.log(`[Core] Initializing cache with strategy: ${config.strategy}`);
8630
- }
8814
+ _Core.log(`Initializing cache with strategy: ${config.strategy}`, verbose);
8631
8815
  await CacheService.initialize(config);
8632
8816
  }
8633
8817
  /**
@@ -8635,9 +8819,7 @@ var Core = class _Core {
8635
8819
  * Always includes LoggerAdapter as failover for console output.
8636
8820
  */
8637
8821
  static async initializeObservability(config, verbose, environment) {
8638
- if (verbose) {
8639
- console.log(`[Core] Initializing observability with provider: ${config.provider ?? "auto"}`);
8640
- }
8822
+ _Core.log(`Initializing observability with provider: ${config.provider ?? "auto"}`, verbose);
8641
8823
  const env = config.environment ?? environment ?? "development";
8642
8824
  _Core._observabilityConfig = { ...config, environment: env };
8643
8825
  const adapters = [];
@@ -8661,11 +8843,10 @@ var Core = class _Core {
8661
8843
  flushInterval: config.flushInterval,
8662
8844
  adapterTimeout: config.flushInterval
8663
8845
  });
8664
- if (verbose) {
8665
- console.log(
8666
- `[Core] ObservabilityService initialized with ${adapters.length} adapters (env: ${env})`
8667
- );
8668
- }
8846
+ _Core.log(
8847
+ `ObservabilityService initialized with ${adapters.length} adapters (env: ${env})`,
8848
+ verbose
8849
+ );
8669
8850
  _Core._coreServices.observability = observability;
8670
8851
  ServiceRegistry.setObservabilityInstance(observability);
8671
8852
  }
@@ -8676,10 +8857,10 @@ var Core = class _Core {
8676
8857
  static initializeRootStore(config, verbose) {
8677
8858
  const isFrontend = typeof window !== "undefined";
8678
8859
  if (isFrontend) {
8679
- if (verbose) console.log("[Core] Using frontend root store (Zustand)");
8860
+ _Core.log("Using frontend root store (Zustand)", verbose);
8680
8861
  _Core._rootStore = store.useRootStore;
8681
8862
  } else {
8682
- if (verbose) console.log("[Core] Creating backend composite store (in-memory)");
8863
+ _Core.log("Creating backend composite store (in-memory)", verbose);
8683
8864
  const backendErrorStore = middleware.ServerErrorMiddleware.createErrorStore({
8684
8865
  maxErrors: config?.maxErrors ?? DEFAULT_MAX_ERRORS,
8685
8866
  onErrorAdded: config?.onError
@@ -8727,11 +8908,11 @@ var Core = class _Core {
8727
8908
  // eslint-disable-next-line complexity
8728
8909
  static async initializeErrorHandler(config, verbose) {
8729
8910
  if (config?.enabled === false) {
8730
- if (verbose) console.log("[Core] Error handler disabled by configuration");
8911
+ _Core.log("Error handler disabled by configuration", verbose);
8731
8912
  return;
8732
8913
  }
8733
8914
  _Core._errorConfig = config ?? {};
8734
- if (verbose) console.log("[Core] Initializing global error handler...");
8915
+ _Core.log("Initializing global error handler...", verbose);
8735
8916
  errors.initializeErrorSystem({
8736
8917
  defaultLocale: _Core._errorConfig.localization?.defaultLocale,
8737
8918
  fallbackLocale: _Core._errorConfig.localization?.fallbackLocale,
@@ -8753,16 +8934,15 @@ var Core = class _Core {
8753
8934
  const { errors } = event.data;
8754
8935
  if (errors && errors.length > 0) {
8755
8936
  _Core._rootStore.getState().errors.addErrors(errors);
8756
- if (verbose) console.log(`[Core] Added ${errors.length} error(s) to store`);
8937
+ _Core.log(`Added ${errors.length} error(s) to store`, verbose);
8757
8938
  }
8758
8939
  } catch (e) {
8759
- console.error("[Core] Failed to handle error event:", e);
8940
+ _Core.logger.error("Failed to handle error event", { error: e });
8760
8941
  }
8761
8942
  }
8762
8943
  );
8763
8944
  _Core._eventCleanupFns.push(errorEventCleanup);
8764
- if (verbose)
8765
- console.log("[Core] Global error handler initialized with CoreEventManager integration");
8945
+ _Core.log("Global error handler initialized with CoreEventManager integration", verbose);
8766
8946
  if (_Core._errorConfig.httpHandler !== false) {
8767
8947
  _Core.createHttpErrorHandler(_Core._errorConfig, verbose);
8768
8948
  }
@@ -8784,11 +8964,11 @@ var Core = class _Core {
8784
8964
  switch (runtime) {
8785
8965
  case "express":
8786
8966
  _Core._httpErrorHandler = middleware.ServerErrorMiddleware.createHttpErrorHandler(httpConfig);
8787
- if (verbose) console.log("[Core] Created Express error handler middleware");
8967
+ _Core.log("Created Express error handler middleware", verbose);
8788
8968
  break;
8789
8969
  case "nestjs":
8790
8970
  _Core._httpErrorHandler = middleware.ServerErrorMiddleware.createNestJsExceptionFilter(httpConfig);
8791
- if (verbose) console.log("[Core] Created NestJS exception filter");
8971
+ _Core.log("Created NestJS exception filter", verbose);
8792
8972
  break;
8793
8973
  case "nextjs":
8794
8974
  _Core._httpErrorHandler = {
@@ -8800,7 +8980,7 @@ var Core = class _Core {
8800
8980
  /** For Pages Router: export default createNextApiErrorHandler()(handler) */
8801
8981
  createNextApiErrorHandler: /* @__PURE__ */ __name((overrideConfig = {}) => middleware.ServerErrorMiddleware.createNextApiErrorHandler({ ...httpConfig, ...overrideConfig }), "createNextApiErrorHandler")
8802
8982
  };
8803
- if (verbose) console.log("[Core] Created Next.js error handlers");
8983
+ _Core.log("Created Next.js error handlers", verbose);
8804
8984
  break;
8805
8985
  case "node":
8806
8986
  case "bun":
@@ -8817,15 +8997,15 @@ var Core = class _Core {
8817
8997
  /** Create middleware for use with native http */
8818
8998
  createMiddleware: /* @__PURE__ */ __name(() => middleware.ServerErrorMiddleware.createHttpErrorHandler(httpConfig), "createMiddleware")
8819
8999
  };
8820
- if (verbose) console.log(`[Core] Created ${runtime} error handler`);
9000
+ _Core.log(`Created ${runtime} error handler`, verbose);
8821
9001
  break;
8822
9002
  case "browser":
8823
9003
  _Core._httpErrorHandler = null;
8824
- if (verbose) console.log("[Core] Skipping HTTP handler for browser runtime");
9004
+ _Core.log("Skipping HTTP handler for browser runtime", verbose);
8825
9005
  break;
8826
9006
  default:
8827
9007
  _Core._httpErrorHandler = middleware.ServerErrorMiddleware.createHttpErrorHandler(httpConfig);
8828
- if (verbose) console.log(`[Core] Created generic HTTP error handler for ${runtime}`);
9008
+ _Core.log(`Created generic HTTP error handler for ${runtime}`, verbose);
8829
9009
  }
8830
9010
  }
8831
9011
  /**
@@ -8839,9 +9019,7 @@ var Core = class _Core {
8839
9019
  if (!_Core._errorHandler) {
8840
9020
  return;
8841
9021
  }
8842
- if (verbose) {
8843
- console.log("[Core] Subscribing to error events...");
8844
- }
9022
+ _Core.log("Subscribing to error events...", verbose);
8845
9023
  const cleanupSystemError = CoreEventManager.on(core.CORE_EVENTS.SYSTEM.ERROR, (event) => {
8846
9024
  if (_Core._errorHandler && event.data?.errors?.length) {
8847
9025
  for (const err of event.data.errors) {
@@ -8907,21 +9085,17 @@ var Core = class _Core {
8907
9085
  }
8908
9086
  );
8909
9087
  _Core._eventCleanupFns.push(cleanupNotificationError);
8910
- if (verbose) {
8911
- console.log("[Core] Subscribed to backend error events (database, storage, notification)");
8912
- }
9088
+ _Core.log("Subscribed to backend error events (database, storage, notification)", verbose);
8913
9089
  }
8914
- if (verbose) {
8915
- const eventTypes = ["system", "entity", "api", "validation", "auth"];
8916
- if (_Core.isRuntimeCompatible("backend")) {
8917
- eventTypes.push("database", "storage", "notification");
8918
- }
8919
- console.log(`[Core] Subscribed to error events: ${eventTypes.join(", ")}`);
9090
+ const eventTypes = ["system", "entity", "api", "validation", "auth"];
9091
+ if (_Core.isRuntimeCompatible("backend")) {
9092
+ eventTypes.push("database", "storage", "notification");
8920
9093
  }
9094
+ _Core.log(`Subscribed to error events: ${eventTypes.join(", ")}`, verbose);
8921
9095
  }
8922
9096
  /** Handle fetch flags error and return empty flags */
8923
9097
  static handleFetchFlagsError(error, config, verbose) {
8924
- if (verbose) console.error("[Core] Failed to fetch feature flags:", error);
9098
+ if (verbose) _Core.logger.error("Failed to fetch feature flags", { error });
8925
9099
  const packageError = error instanceof errors.BaseError ? error : new errors.CorePackageError(
8926
9100
  error instanceof Error ? error.message : String(error),
8927
9101
  types.ERROR_CODES.CORE_FEATURE_FLAG_PROVIDER_ERROR,
@@ -8954,7 +9128,7 @@ var Core = class _Core {
8954
9128
  */
8955
9129
  static async initializeFeatureFlags(config, verbose) {
8956
9130
  if (config?.enabled === false) {
8957
- if (verbose) console.log("[Core] Feature flags disabled by configuration");
9131
+ _Core.log("Feature flags disabled by configuration", verbose);
8958
9132
  return;
8959
9133
  }
8960
9134
  if (!_Core._rootStore) {
@@ -8964,7 +9138,7 @@ var Core = class _Core {
8964
9138
  );
8965
9139
  }
8966
9140
  _Core._flagConfig = config ?? {};
8967
- if (verbose) console.log("[Core] Initializing feature flags from root store...");
9141
+ _Core.log("Initializing feature flags from root store...", verbose);
8968
9142
  const state = _Core._rootStore.getState();
8969
9143
  await state.featureFlags.initialize({
8970
9144
  defaults: _Core._flagConfig.defaults,
@@ -8973,11 +9147,12 @@ var Core = class _Core {
8973
9147
  onFlagChange: _Core._flagConfig.onFlagChange,
8974
9148
  onError: _Core._flagConfig.onError
8975
9149
  });
8976
- if (verbose) console.log("[Core] Feature flags initialized");
9150
+ _Core.log("Feature flags initialized", verbose);
8977
9151
  }
8978
9152
  };
8979
9153
 
8980
9154
  // src/init/nestjs/CoreModule.ts
9155
+ init_DbService();
8981
9156
  var CORE_OPTIONS = Symbol("CORE_OPTIONS");
8982
9157
  var DB_SERVICE = Symbol("DB_SERVICE");
8983
9158
  var CoreModule = class _CoreModule {