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