@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.
- package/README.md +248 -183
- package/dist/domain/example/FrontendExampleDomainService.d.ts.map +1 -1
- package/dist/domain/featureFlags/providers/database.d.ts +6 -1
- package/dist/domain/featureFlags/providers/database.d.ts.map +1 -1
- package/dist/entry-backend.d.ts +3 -1
- package/dist/entry-backend.d.ts.map +1 -1
- package/dist/entry-backend.js +3145 -3325
- package/dist/entry-backend.js.map +1 -1
- package/dist/entry-backend.mjs +2732 -2899
- package/dist/entry-backend.mjs.map +1 -1
- package/dist/entry-frontend.js +1594 -1406
- package/dist/entry-frontend.js.map +1 -1
- package/dist/entry-frontend.mjs +1568 -1379
- package/dist/entry-frontend.mjs.map +1 -1
- package/dist/index.js +3130 -3318
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3133 -3308
- package/dist/index.mjs.map +1 -1
- package/dist/init/CoreInitializer.d.ts +9 -8
- package/dist/init/CoreInitializer.d.ts.map +1 -1
- package/dist/init/ServiceRegistry.d.ts.map +1 -1
- package/dist/init/nestjs/index.js +1511 -1336
- package/dist/init/nestjs/index.js.map +1 -1
- package/dist/init/nestjs/index.mjs +1527 -1352
- package/dist/init/nestjs/index.mjs.map +1 -1
- package/dist/models/example/ExampleRepository.d.ts +45 -3
- package/dist/models/example/ExampleRepository.d.ts.map +1 -1
- package/dist/models/featureFlags/FeatureFlagRepository.d.ts +72 -471
- package/dist/models/featureFlags/FeatureFlagRepository.d.ts.map +1 -1
- package/dist/services/DbService.d.ts +2 -0
- package/dist/services/DbService.d.ts.map +1 -1
- package/dist/services/NotificationService.d.ts +2 -0
- package/dist/services/NotificationService.d.ts.map +1 -1
- package/dist/services/StorageService.d.ts +2 -0
- package/dist/services/StorageService.d.ts.map +1 -1
- package/dist/utils/common/id.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/entry-frontend.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var config = require('@plyaz/config');
|
|
4
|
+
var types = require('@plyaz/types');
|
|
5
|
+
var events = require('events');
|
|
4
6
|
var db = require('@plyaz/db');
|
|
5
7
|
var errors = require('@plyaz/errors');
|
|
6
8
|
var errors$1 = require('@plyaz/types/errors');
|
|
7
9
|
var core = require('@plyaz/types/core');
|
|
8
|
-
var
|
|
9
|
-
var
|
|
10
|
-
var
|
|
10
|
+
var storage = require('@plyaz/storage');
|
|
11
|
+
var notifications = require('@plyaz/notifications');
|
|
12
|
+
var logger$1 = require('@plyaz/logger');
|
|
11
13
|
var api = require('@plyaz/api');
|
|
12
14
|
var common = require('@nestjs/common');
|
|
13
15
|
var rxjs = require('rxjs');
|
|
14
16
|
var features = require('@plyaz/types/features');
|
|
15
|
-
var storage = require('@plyaz/storage');
|
|
16
|
-
var notifications = require('@plyaz/notifications');
|
|
17
17
|
var store = require('@plyaz/store');
|
|
18
18
|
var observability = require('@plyaz/types/observability');
|
|
19
19
|
var examples = require('@plyaz/types/examples');
|
|
@@ -50,7 +50,15 @@ var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
|
50
50
|
// @plyaz package - Built with tsup
|
|
51
51
|
var __defProp = Object.defineProperty;
|
|
52
52
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
53
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
53
54
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
55
|
+
var __esm = (fn, res) => function __init() {
|
|
56
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
57
|
+
};
|
|
58
|
+
var __export = (target, all) => {
|
|
59
|
+
for (var name in all)
|
|
60
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
61
|
+
};
|
|
54
62
|
var __decorateClass = (decorators, target, key, kind) => {
|
|
55
63
|
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
56
64
|
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
@@ -67,29 +75,26 @@ function hashString(str) {
|
|
|
67
75
|
}
|
|
68
76
|
return hash >>> 0;
|
|
69
77
|
}
|
|
70
|
-
__name(hashString, "hashString");
|
|
71
78
|
function isInRollout(identifier, percentage) {
|
|
72
79
|
if (percentage >= config.MATH_CONSTANTS.PERCENTAGE_MAX) return true;
|
|
73
80
|
if (percentage <= 0) return false;
|
|
74
81
|
const hash = hashString(identifier);
|
|
75
82
|
return hash % config.MATH_CONSTANTS.PERCENTAGE_MAX < percentage;
|
|
76
83
|
}
|
|
77
|
-
__name(isInRollout, "isInRollout");
|
|
78
84
|
function createRolloutIdentifier(featureKey, userId) {
|
|
79
85
|
const trimmedUserId = userId?.trim();
|
|
80
86
|
const effectiveUserId = trimmedUserId && trimmedUserId.length > 0 ? trimmedUserId : "anonymous";
|
|
81
87
|
return `${featureKey}:${effectiveUserId}`;
|
|
82
88
|
}
|
|
83
|
-
|
|
89
|
+
var init_hash = __esm({
|
|
90
|
+
"src/utils/common/hash.ts"() {
|
|
91
|
+
__name(hashString, "hashString");
|
|
92
|
+
__name(isInRollout, "isInRollout");
|
|
93
|
+
__name(createRolloutIdentifier, "createRolloutIdentifier");
|
|
94
|
+
}
|
|
95
|
+
});
|
|
84
96
|
|
|
85
97
|
// src/utils/common/id.ts
|
|
86
|
-
var RANDOM_ID_RADIX = 36;
|
|
87
|
-
var RANDOM_ID_SLICE_START = 2;
|
|
88
|
-
var SHORT_ID_LENGTH = 8;
|
|
89
|
-
var HEX_RADIX = 16;
|
|
90
|
-
var HEX_SLICE_START = 2;
|
|
91
|
-
var HEX_SLICE_END = 18;
|
|
92
|
-
var SPAN_ID_LENGTH = 16;
|
|
93
98
|
function generateId() {
|
|
94
99
|
const cryptoApi = globalThis.crypto;
|
|
95
100
|
if (cryptoApi?.randomUUID) {
|
|
@@ -97,7 +102,6 @@ function generateId() {
|
|
|
97
102
|
}
|
|
98
103
|
return `${Date.now()}-${Math.random().toString(RANDOM_ID_RADIX).slice(RANDOM_ID_SLICE_START)}`;
|
|
99
104
|
}
|
|
100
|
-
__name(generateId, "generateId");
|
|
101
105
|
function generateShortId() {
|
|
102
106
|
const cryptoApi = globalThis.crypto;
|
|
103
107
|
if (cryptoApi?.randomUUID) {
|
|
@@ -105,11 +109,9 @@ function generateShortId() {
|
|
|
105
109
|
}
|
|
106
110
|
return Math.random().toString(RANDOM_ID_RADIX).slice(RANDOM_ID_SLICE_START, RANDOM_ID_SLICE_START + SHORT_ID_LENGTH);
|
|
107
111
|
}
|
|
108
|
-
__name(generateShortId, "generateShortId");
|
|
109
112
|
function generateCorrelationId() {
|
|
110
113
|
return `${Date.now()}-${generateShortId()}`;
|
|
111
114
|
}
|
|
112
|
-
__name(generateCorrelationId, "generateCorrelationId");
|
|
113
115
|
function generateTraceId() {
|
|
114
116
|
const cryptoApi = globalThis.crypto;
|
|
115
117
|
if (cryptoApi?.randomUUID) {
|
|
@@ -117,7 +119,6 @@ function generateTraceId() {
|
|
|
117
119
|
}
|
|
118
120
|
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);
|
|
119
121
|
}
|
|
120
|
-
__name(generateTraceId, "generateTraceId");
|
|
121
122
|
function generateSpanId() {
|
|
122
123
|
const cryptoApi = globalThis.crypto;
|
|
123
124
|
if (cryptoApi?.randomUUID) {
|
|
@@ -125,20 +126,34 @@ function generateSpanId() {
|
|
|
125
126
|
}
|
|
126
127
|
return Math.random().toString(HEX_RADIX).substring(HEX_SLICE_START, HEX_SLICE_END);
|
|
127
128
|
}
|
|
128
|
-
|
|
129
|
+
var RANDOM_ID_RADIX, RANDOM_ID_SLICE_START, SHORT_ID_LENGTH, HEX_RADIX, HEX_SLICE_START, HEX_SLICE_END, SPAN_ID_LENGTH;
|
|
130
|
+
var init_id = __esm({
|
|
131
|
+
"src/utils/common/id.ts"() {
|
|
132
|
+
RANDOM_ID_RADIX = 36;
|
|
133
|
+
RANDOM_ID_SLICE_START = 2;
|
|
134
|
+
SHORT_ID_LENGTH = 8;
|
|
135
|
+
HEX_RADIX = 16;
|
|
136
|
+
HEX_SLICE_START = 2;
|
|
137
|
+
HEX_SLICE_END = 18;
|
|
138
|
+
SPAN_ID_LENGTH = 16;
|
|
139
|
+
__name(generateId, "generateId");
|
|
140
|
+
__name(generateShortId, "generateShortId");
|
|
141
|
+
__name(generateCorrelationId, "generateCorrelationId");
|
|
142
|
+
__name(generateTraceId, "generateTraceId");
|
|
143
|
+
__name(generateSpanId, "generateSpanId");
|
|
144
|
+
}
|
|
145
|
+
});
|
|
129
146
|
function isStringFalsy(value) {
|
|
130
147
|
if (value === "") return true;
|
|
131
148
|
const lower = value.toLowerCase().trim();
|
|
132
149
|
return ["false", "no", "0", "off", "disabled"].includes(lower);
|
|
133
150
|
}
|
|
134
|
-
__name(isStringFalsy, "isStringFalsy");
|
|
135
151
|
function isObjectTruthy(value) {
|
|
136
152
|
if (Array.isArray(value)) return value.length > 0;
|
|
137
153
|
if (value instanceof Map || value instanceof Set) return value.size > 0;
|
|
138
154
|
if (value.constructor === Object) return Object.keys(value).length > 0;
|
|
139
155
|
return true;
|
|
140
156
|
}
|
|
141
|
-
__name(isObjectTruthy, "isObjectTruthy");
|
|
142
157
|
function isTruthy(value) {
|
|
143
158
|
if (value === null || value === void 0) return false;
|
|
144
159
|
switch (typeof value) {
|
|
@@ -157,747 +172,1215 @@ function isTruthy(value) {
|
|
|
157
172
|
return false;
|
|
158
173
|
}
|
|
159
174
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
constructor() {
|
|
166
|
-
this.emitter = new events.EventEmitter();
|
|
167
|
-
this.subscriptions = /* @__PURE__ */ new Map();
|
|
168
|
-
this.emitter.setMaxListeners(MAX_EVENT_LISTENERS);
|
|
169
|
-
}
|
|
170
|
-
static {
|
|
171
|
-
__name(this, "CoreEventManagerClass");
|
|
172
|
-
}
|
|
173
|
-
emit(eventType, data) {
|
|
174
|
-
const [scope] = eventType.split(":");
|
|
175
|
-
const event = {
|
|
176
|
-
type: eventType,
|
|
177
|
-
scope: scope ?? "system",
|
|
178
|
-
timestamp: Date.now(),
|
|
179
|
-
correlationId: this.getCorrelationId(),
|
|
180
|
-
data
|
|
181
|
-
};
|
|
182
|
-
this.emitter.emit(eventType, event);
|
|
183
|
-
this.emitter.emit("*", event);
|
|
184
|
-
return true;
|
|
185
|
-
}
|
|
186
|
-
on(eventType, handler) {
|
|
187
|
-
this.emitter.on(eventType, handler);
|
|
188
|
-
if (!this.subscriptions.has(eventType)) {
|
|
189
|
-
this.subscriptions.set(eventType, /* @__PURE__ */ new Set());
|
|
190
|
-
}
|
|
191
|
-
this.subscriptions.get(eventType).add(handler);
|
|
192
|
-
return () => {
|
|
193
|
-
this.emitter.off(eventType, handler);
|
|
194
|
-
this.subscriptions.get(eventType)?.delete(handler);
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
once(eventType, handler) {
|
|
198
|
-
const wrappedHandler = /* @__PURE__ */ __name((event) => {
|
|
199
|
-
handler(event);
|
|
200
|
-
this.emitter.off(eventType, wrappedHandler);
|
|
201
|
-
this.subscriptions.get(eventType)?.delete(wrappedHandler);
|
|
202
|
-
}, "wrappedHandler");
|
|
203
|
-
this.emitter.on(eventType, wrappedHandler);
|
|
204
|
-
if (!this.subscriptions.has(eventType)) {
|
|
205
|
-
this.subscriptions.set(eventType, /* @__PURE__ */ new Set());
|
|
206
|
-
}
|
|
207
|
-
this.subscriptions.get(eventType).add(wrappedHandler);
|
|
208
|
-
return () => {
|
|
209
|
-
this.emitter.off(eventType, wrappedHandler);
|
|
210
|
-
this.subscriptions.get(eventType)?.delete(wrappedHandler);
|
|
211
|
-
};
|
|
175
|
+
var init_values = __esm({
|
|
176
|
+
"src/utils/common/values.ts"() {
|
|
177
|
+
__name(isStringFalsy, "isStringFalsy");
|
|
178
|
+
__name(isObjectTruthy, "isObjectTruthy");
|
|
179
|
+
__name(isTruthy, "isTruthy");
|
|
212
180
|
}
|
|
213
|
-
|
|
214
|
-
* Subscribe to all events in a scope
|
|
215
|
-
*
|
|
216
|
-
* @param scope - Event scope to listen to (e.g., CoreEventScope.AUTH)
|
|
217
|
-
* @param handler - Event handler
|
|
218
|
-
* @returns Unsubscribe function
|
|
219
|
-
*
|
|
220
|
-
* @example
|
|
221
|
-
* ```typescript
|
|
222
|
-
* CoreEventManager.onScope(CoreEventScope.AUTH, (event) => {
|
|
223
|
-
* // Handles auth:login, auth:logout, auth:tokenRefresh, etc.
|
|
224
|
-
* console.log(`Auth event: ${event.type}`, event.data);
|
|
225
|
-
* });
|
|
226
|
-
* ```
|
|
227
|
-
*/
|
|
228
|
-
onScope(scope, handler) {
|
|
229
|
-
const wrappedHandler = /* @__PURE__ */ __name((event) => {
|
|
230
|
-
if (event.scope === scope) {
|
|
231
|
-
handler(event);
|
|
232
|
-
}
|
|
233
|
-
}, "wrappedHandler");
|
|
234
|
-
return this.on("*", wrappedHandler);
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Remove a specific handler from an event
|
|
238
|
-
*
|
|
239
|
-
* @param eventType - Event type
|
|
240
|
-
* @param handler - Handler to remove
|
|
241
|
-
*/
|
|
242
|
-
off(eventType, handler) {
|
|
243
|
-
this.emitter.off(eventType, handler);
|
|
244
|
-
this.subscriptions.get(eventType)?.delete(handler);
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Dispose all subscriptions
|
|
248
|
-
*/
|
|
249
|
-
dispose() {
|
|
250
|
-
this.subscriptions.forEach((handlers, eventType) => {
|
|
251
|
-
handlers.forEach((handler) => this.emitter.off(eventType, handler));
|
|
252
|
-
});
|
|
253
|
-
this.subscriptions.clear();
|
|
254
|
-
this.emitter.removeAllListeners();
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* Generate correlation ID for event tracing
|
|
258
|
-
*/
|
|
259
|
-
getCorrelationId() {
|
|
260
|
-
return generateCorrelationId();
|
|
261
|
-
}
|
|
262
|
-
};
|
|
263
|
-
var CoreEventManager = new CoreEventManagerClass();
|
|
181
|
+
});
|
|
264
182
|
|
|
265
|
-
// src/
|
|
266
|
-
var
|
|
267
|
-
|
|
268
|
-
users: ["password_hash", "phone_number", "date_of_birth"],
|
|
269
|
-
// KYC sensitive data
|
|
270
|
-
"backoffice.kyc_submissions": ["tax_id", "address_line1", "address_line2"],
|
|
271
|
-
// Connected accounts OAuth tokens
|
|
272
|
-
connected_accounts: ["access_token_encrypted", "refresh_token_encrypted", "wallet_address"],
|
|
273
|
-
// Payment payout accounts
|
|
274
|
-
user_payout_accounts: ["provider_account_id", "account_holder_name"],
|
|
275
|
-
// Sessions
|
|
276
|
-
sessions: ["token"]
|
|
277
|
-
};
|
|
278
|
-
var DEFAULT_CACHE_TTL_SECONDS = 300;
|
|
279
|
-
var DEFAULT_AUDIT_RETENTION_DAYS = 180;
|
|
280
|
-
var DEFAULT_POOL_SIZE = 10;
|
|
281
|
-
var DEFAULT_CONFIG = {
|
|
282
|
-
adapter: "sql",
|
|
283
|
-
cache: {
|
|
284
|
-
enabled: true,
|
|
285
|
-
provider: "memory",
|
|
286
|
-
ttl: DEFAULT_CACHE_TTL_SECONDS
|
|
287
|
-
},
|
|
288
|
-
softDelete: {
|
|
289
|
-
enabled: true,
|
|
290
|
-
field: "deleted_at",
|
|
291
|
-
excludeTables: [
|
|
292
|
-
"audit_logs",
|
|
293
|
-
"audit.audit_logs",
|
|
294
|
-
"audit.feature_flag_evaluations",
|
|
295
|
-
"feature_flag_evaluations",
|
|
296
|
-
"notification_events",
|
|
297
|
-
"payments_activity",
|
|
298
|
-
"campaign_analytics",
|
|
299
|
-
"admin_actions",
|
|
300
|
-
"backoffice.admin_actions"
|
|
301
|
-
]
|
|
302
|
-
},
|
|
303
|
-
audit: {
|
|
304
|
-
enabled: true,
|
|
305
|
-
// Enabled by default for compliance
|
|
306
|
-
retentionDays: DEFAULT_AUDIT_RETENTION_DAYS,
|
|
307
|
-
excludeFields: ["password_hash", "api_key_hash", "token_hash"],
|
|
308
|
-
excludeTables: ["audit_logs", "audit.audit_logs"],
|
|
309
|
-
// Don't audit the audit table itself
|
|
310
|
-
schema: "audit",
|
|
311
|
-
// Use dedicated audit schema
|
|
312
|
-
usePartitionedTables: true
|
|
313
|
-
// Use daily partitioned tables (audit_log_yyyy_mm_dd)
|
|
314
|
-
}
|
|
315
|
-
// NOTE: Encryption requires key from environment or DbService.initialize()
|
|
316
|
-
// Enable via: DbService.initialize({ encryption: { enabled: true, key: process.env.ENCRYPTION_KEY!, fields: DEFAULT_ENCRYPTION_FIELDS } })
|
|
317
|
-
};
|
|
318
|
-
var TABLE_REGISTRY = {
|
|
319
|
-
// Migration 008: Feature Flags (custom ID column 'key')
|
|
320
|
-
feature_flags: { idColumn: "key" },
|
|
321
|
-
// Test Feature Flags (custom ID column 'key')
|
|
322
|
-
test_feature_flags: { idColumn: "key" },
|
|
323
|
-
// Migration 010: Universal Analytics (custom ID column 'user_id')
|
|
324
|
-
user_analytics_summary: { idColumn: "user_id" }
|
|
325
|
-
};
|
|
326
|
-
var DbService = class _DbService {
|
|
327
|
-
constructor() {
|
|
328
|
-
this.databaseService = null;
|
|
329
|
-
this.namedAdapters = /* @__PURE__ */ new Map();
|
|
330
|
-
this.config = null;
|
|
331
|
-
this.initialized = false;
|
|
332
|
-
}
|
|
333
|
-
static {
|
|
334
|
-
__name(this, "DbService");
|
|
335
|
-
}
|
|
336
|
-
static {
|
|
337
|
-
this.instance = null;
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* Emits a database error event via CoreEventManager.
|
|
341
|
-
* Called when database operations fail to integrate with global error handling.
|
|
342
|
-
*
|
|
343
|
-
* @param error - The error that occurred
|
|
344
|
-
* @param operation - The operation that failed (e.g., 'transaction', 'query', 'healthCheck')
|
|
345
|
-
* @param table - Optional table name involved in the operation
|
|
346
|
-
* @param query - Optional query string that failed
|
|
347
|
-
* @param recoverable - Whether the error is recoverable (default: false)
|
|
348
|
-
*/
|
|
349
|
-
emitDatabaseError(error, operation, options) {
|
|
350
|
-
const payload = {
|
|
351
|
-
error,
|
|
352
|
-
operation,
|
|
353
|
-
table: options?.table,
|
|
354
|
-
query: options?.query,
|
|
355
|
-
recoverable: options?.recoverable ?? false
|
|
356
|
-
};
|
|
357
|
-
CoreEventManager.emit(core.CORE_EVENTS.DATABASE.ERROR, payload);
|
|
183
|
+
// src/utils/common/object.ts
|
|
184
|
+
var init_object = __esm({
|
|
185
|
+
"src/utils/common/object.ts"() {
|
|
358
186
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
* @returns {DbService} The singleton instance
|
|
363
|
-
*/
|
|
364
|
-
static getInstance() {
|
|
365
|
-
_DbService.instance ??= new _DbService();
|
|
366
|
-
return _DbService.instance;
|
|
187
|
+
});
|
|
188
|
+
var init_validation = __esm({
|
|
189
|
+
"src/utils/common/validation.ts"() {
|
|
367
190
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// src/utils/common/index.ts
|
|
194
|
+
var init_common = __esm({
|
|
195
|
+
"src/utils/common/index.ts"() {
|
|
196
|
+
init_hash();
|
|
197
|
+
init_id();
|
|
198
|
+
init_values();
|
|
199
|
+
init_object();
|
|
200
|
+
init_validation();
|
|
375
201
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
202
|
+
});
|
|
203
|
+
var MAX_EVENT_LISTENERS, CoreEventManagerClass; exports.CoreEventManager = void 0;
|
|
204
|
+
var init_CoreEventManager = __esm({
|
|
205
|
+
"src/events/CoreEventManager.ts"() {
|
|
206
|
+
init_common();
|
|
207
|
+
MAX_EVENT_LISTENERS = 100;
|
|
208
|
+
CoreEventManagerClass = class {
|
|
209
|
+
constructor() {
|
|
210
|
+
this.emitter = new events.EventEmitter();
|
|
211
|
+
this.subscriptions = /* @__PURE__ */ new Map();
|
|
212
|
+
this.emitter.setMaxListeners(MAX_EVENT_LISTENERS);
|
|
213
|
+
}
|
|
214
|
+
static {
|
|
215
|
+
__name(this, "CoreEventManagerClass");
|
|
216
|
+
}
|
|
217
|
+
emit(eventType, data) {
|
|
218
|
+
const [scope] = eventType.split(":");
|
|
219
|
+
const event = {
|
|
220
|
+
type: eventType,
|
|
221
|
+
scope: scope ?? "system",
|
|
222
|
+
timestamp: Date.now(),
|
|
223
|
+
correlationId: this.getCorrelationId(),
|
|
224
|
+
data
|
|
225
|
+
};
|
|
226
|
+
this.emitter.emit(eventType, event);
|
|
227
|
+
this.emitter.emit("*", event);
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
on(eventType, handler) {
|
|
231
|
+
this.emitter.on(eventType, handler);
|
|
232
|
+
if (!this.subscriptions.has(eventType)) {
|
|
233
|
+
this.subscriptions.set(eventType, /* @__PURE__ */ new Set());
|
|
234
|
+
}
|
|
235
|
+
this.subscriptions.get(eventType).add(handler);
|
|
236
|
+
return () => {
|
|
237
|
+
this.emitter.off(eventType, handler);
|
|
238
|
+
this.subscriptions.get(eventType)?.delete(handler);
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
once(eventType, handler) {
|
|
242
|
+
const wrappedHandler = /* @__PURE__ */ __name((event) => {
|
|
243
|
+
handler(event);
|
|
244
|
+
this.emitter.off(eventType, wrappedHandler);
|
|
245
|
+
this.subscriptions.get(eventType)?.delete(wrappedHandler);
|
|
246
|
+
}, "wrappedHandler");
|
|
247
|
+
this.emitter.on(eventType, wrappedHandler);
|
|
248
|
+
if (!this.subscriptions.has(eventType)) {
|
|
249
|
+
this.subscriptions.set(eventType, /* @__PURE__ */ new Set());
|
|
250
|
+
}
|
|
251
|
+
this.subscriptions.get(eventType).add(wrappedHandler);
|
|
252
|
+
return () => {
|
|
253
|
+
this.emitter.off(eventType, wrappedHandler);
|
|
254
|
+
this.subscriptions.get(eventType)?.delete(wrappedHandler);
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Subscribe to all events in a scope
|
|
259
|
+
*
|
|
260
|
+
* @param scope - Event scope to listen to (e.g., CoreEventScope.AUTH)
|
|
261
|
+
* @param handler - Event handler
|
|
262
|
+
* @returns Unsubscribe function
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* ```typescript
|
|
266
|
+
* CoreEventManager.onScope(CoreEventScope.AUTH, (event) => {
|
|
267
|
+
* // Handles auth:login, auth:logout, auth:tokenRefresh, etc.
|
|
268
|
+
* console.log(`Auth event: ${event.type}`, event.data);
|
|
269
|
+
* });
|
|
270
|
+
* ```
|
|
271
|
+
*/
|
|
272
|
+
onScope(scope, handler) {
|
|
273
|
+
const wrappedHandler = /* @__PURE__ */ __name((event) => {
|
|
274
|
+
if (event.scope === scope) {
|
|
275
|
+
handler(event);
|
|
276
|
+
}
|
|
277
|
+
}, "wrappedHandler");
|
|
278
|
+
return this.on("*", wrappedHandler);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Remove a specific handler from an event
|
|
282
|
+
*
|
|
283
|
+
* @param eventType - Event type
|
|
284
|
+
* @param handler - Handler to remove
|
|
285
|
+
*/
|
|
286
|
+
off(eventType, handler) {
|
|
287
|
+
this.emitter.off(eventType, handler);
|
|
288
|
+
this.subscriptions.get(eventType)?.delete(handler);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Dispose all subscriptions
|
|
292
|
+
*/
|
|
293
|
+
dispose() {
|
|
294
|
+
this.subscriptions.forEach((handlers, eventType) => {
|
|
295
|
+
handlers.forEach((handler) => this.emitter.off(eventType, handler));
|
|
296
|
+
});
|
|
297
|
+
this.subscriptions.clear();
|
|
298
|
+
this.emitter.removeAllListeners();
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Generate correlation ID for event tracing
|
|
302
|
+
*/
|
|
303
|
+
getCorrelationId() {
|
|
304
|
+
return generateCorrelationId();
|
|
393
305
|
}
|
|
394
|
-
_DbService.instance = null;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
/**
|
|
398
|
-
* Initializes the database connection
|
|
399
|
-
*
|
|
400
|
-
* @description Sets up the database connection using the provided configuration
|
|
401
|
-
* or environment variables. This method is idempotent - calling it multiple
|
|
402
|
-
* times won't create additional connections.
|
|
403
|
-
*
|
|
404
|
-
* **Environment Variables:**
|
|
405
|
-
* - `DATABASE_URL` - PostgreSQL connection string (for sql/drizzle adapters)
|
|
406
|
-
* - `ENCRYPTION_KEY` - 32-byte encryption key for field encryption (optional)
|
|
407
|
-
* - `SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`, `SUPABASE_ANON_PUBLIC_KEY` - For supabase adapter
|
|
408
|
-
*
|
|
409
|
-
* **Encryption:** If `ENCRYPTION_KEY` env is set, encryption is auto-enabled
|
|
410
|
-
* using `DEFAULT_ENCRYPTION_FIELDS`. Override via config.encryption.
|
|
411
|
-
*
|
|
412
|
-
* @param {DbServiceConfig} [config] - Optional configuration
|
|
413
|
-
* @returns {Promise<DbService>} The initialized DbService instance
|
|
414
|
-
* @throws {DatabasePackageError} When configuration is invalid or connection fails
|
|
415
|
-
*
|
|
416
|
-
* @example
|
|
417
|
-
* ```typescript
|
|
418
|
-
* // Minimal - uses DATABASE_URL env, auto-enables encryption if ENCRYPTION_KEY set
|
|
419
|
-
* await DbService.initialize();
|
|
420
|
-
*
|
|
421
|
-
* // With explicit encryption
|
|
422
|
-
* await DbService.initialize({
|
|
423
|
-
* adapter: 'sql',
|
|
424
|
-
* encryption: {
|
|
425
|
-
* enabled: true,
|
|
426
|
-
* key: 'your-32-byte-encryption-key-here!!',
|
|
427
|
-
* fields: DEFAULT_ENCRYPTION_FIELDS,
|
|
428
|
-
* },
|
|
429
|
-
* });
|
|
430
|
-
*
|
|
431
|
-
* // Or via Core.initialize() with env:
|
|
432
|
-
* await Core.initialize({
|
|
433
|
-
* env: { ENCRYPTION_KEY: '...' },
|
|
434
|
-
* db: { adapter: 'sql' },
|
|
435
|
-
* });
|
|
436
|
-
* ```
|
|
437
|
-
*/
|
|
438
|
-
/** Build encryption config from user config */
|
|
439
|
-
static buildEncryptionConfig(config) {
|
|
440
|
-
const encryption = config?.encryption;
|
|
441
|
-
if (!encryption?.key) return void 0;
|
|
442
|
-
return {
|
|
443
|
-
enabled: encryption.enabled ?? true,
|
|
444
|
-
key: encryption.key,
|
|
445
|
-
fields: encryption.fields ?? DEFAULT_ENCRYPTION_FIELDS,
|
|
446
|
-
algorithm: encryption.algorithm ?? "aes-256-gcm"
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
/** Merge user config with defaults */
|
|
450
|
-
static mergeConfig(config) {
|
|
451
|
-
return {
|
|
452
|
-
...DEFAULT_CONFIG,
|
|
453
|
-
...config,
|
|
454
|
-
softDelete: { ...DEFAULT_CONFIG.softDelete, ...config?.softDelete },
|
|
455
|
-
cache: { ...DEFAULT_CONFIG.cache, ...config?.cache },
|
|
456
|
-
audit: { ...DEFAULT_CONFIG.audit, ...config?.audit },
|
|
457
|
-
encryption: _DbService.buildEncryptionConfig(config)
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
/** Initialize named adapters */
|
|
461
|
-
async initializeNamedAdapters(mergedConfig) {
|
|
462
|
-
if (!mergedConfig.adapters) return;
|
|
463
|
-
for (const [name, adapterConfig] of Object.entries(mergedConfig.adapters)) {
|
|
464
|
-
const namedDbConfig = this.buildDatabaseConfig({
|
|
465
|
-
...mergedConfig,
|
|
466
|
-
adapter: adapterConfig.adapter,
|
|
467
|
-
drizzle: adapterConfig.drizzle,
|
|
468
|
-
supabase: adapterConfig.supabase,
|
|
469
|
-
sql: adapterConfig.sql
|
|
470
|
-
});
|
|
471
|
-
const namedService = await db.createDatabaseService(namedDbConfig);
|
|
472
|
-
this.namedAdapters.set(name, namedService);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
static async initialize(config) {
|
|
476
|
-
const instance = _DbService.getInstance();
|
|
477
|
-
if (instance.initialized) {
|
|
478
|
-
return instance;
|
|
479
|
-
}
|
|
480
|
-
const mergedConfig = _DbService.mergeConfig(config);
|
|
481
|
-
instance.config = mergedConfig;
|
|
482
|
-
const dbConfig = instance.buildDatabaseConfig(mergedConfig);
|
|
483
|
-
instance.databaseService = await db.createDatabaseService(dbConfig);
|
|
484
|
-
await instance.initializeNamedAdapters(mergedConfig);
|
|
485
|
-
instance.initialized = true;
|
|
486
|
-
return instance;
|
|
487
|
-
}
|
|
488
|
-
/**
|
|
489
|
-
* Builds the DatabaseServiceConfig based on the adapter type
|
|
490
|
-
* @private
|
|
491
|
-
*/
|
|
492
|
-
/** Builds adapter-specific config based on adapter type */
|
|
493
|
-
buildAdapterConfig(adapter, config) {
|
|
494
|
-
const builders = {
|
|
495
|
-
drizzle: /* @__PURE__ */ __name(() => this.buildDrizzleConfig(config), "drizzle"),
|
|
496
|
-
supabase: /* @__PURE__ */ __name(() => this.buildSupabaseConfig(config), "supabase"),
|
|
497
|
-
sql: /* @__PURE__ */ __name(() => this.buildSqlConfig(config), "sql")
|
|
498
|
-
};
|
|
499
|
-
const builder = builders[adapter];
|
|
500
|
-
if (!builder) {
|
|
501
|
-
throw new errors.DatabasePackageError(
|
|
502
|
-
`Unsupported adapter type: ${adapter}`,
|
|
503
|
-
errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
|
|
504
|
-
);
|
|
505
|
-
}
|
|
506
|
-
return builder();
|
|
507
|
-
}
|
|
508
|
-
/** Builds soft delete extension config */
|
|
509
|
-
buildSoftDeleteExtension(config) {
|
|
510
|
-
if (!config.softDelete?.enabled) return void 0;
|
|
511
|
-
return {
|
|
512
|
-
enabled: true,
|
|
513
|
-
field: config.softDelete.field ?? "deleted_at",
|
|
514
|
-
excludeTables: config.softDelete.excludeTables
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
/** Builds cache extension config */
|
|
518
|
-
buildCacheExtension(config) {
|
|
519
|
-
if (!config.cache?.enabled) return void 0;
|
|
520
|
-
return {
|
|
521
|
-
enabled: true,
|
|
522
|
-
ttl: config.cache.ttl ?? DEFAULT_CACHE_TTL_SECONDS,
|
|
523
|
-
provider: config.cache.provider ?? "memory",
|
|
524
|
-
invalidation: config.cache.invalidation ?? "write"
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
/** Builds audit extension config */
|
|
528
|
-
buildAuditExtension(config) {
|
|
529
|
-
if (!config.audit?.enabled) return void 0;
|
|
530
|
-
return {
|
|
531
|
-
enabled: true,
|
|
532
|
-
retentionDays: config.audit.retentionDays ?? DEFAULT_AUDIT_RETENTION_DAYS,
|
|
533
|
-
excludeFields: config.audit.excludeFields,
|
|
534
|
-
excludeTables: config.audit.excludeTables,
|
|
535
|
-
schema: config.audit.schema ?? "audit",
|
|
536
|
-
usePartitionedTables: config.audit.usePartitionedTables ?? true
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
/** Builds encryption extension config */
|
|
540
|
-
buildEncryptionExtension(config) {
|
|
541
|
-
if (!config.encryption?.enabled || !config.encryption.key) return void 0;
|
|
542
|
-
return {
|
|
543
|
-
enabled: true,
|
|
544
|
-
key: config.encryption.key,
|
|
545
|
-
fields: config.encryption.fields ?? {},
|
|
546
|
-
algorithm: config.encryption.algorithm
|
|
547
306
|
};
|
|
307
|
+
exports.CoreEventManager = new CoreEventManagerClass();
|
|
548
308
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
"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).",
|
|
574
|
-
errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
|
|
575
|
-
);
|
|
576
|
-
}
|
|
577
|
-
const tableIdColumns = this.buildTableIdColumns();
|
|
578
|
-
return {
|
|
579
|
-
connectionString,
|
|
580
|
-
poolSize: config.drizzle?.poolSize ?? DEFAULT_POOL_SIZE,
|
|
581
|
-
ssl: config.drizzle?.ssl,
|
|
582
|
-
schema: config.drizzle?.schema,
|
|
583
|
-
// Pass through schema configuration
|
|
584
|
-
tableIdColumns
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// src/services/DbService.ts
|
|
312
|
+
var DbService_exports = {};
|
|
313
|
+
__export(DbService_exports, {
|
|
314
|
+
DEFAULT_ENCRYPTION_FIELDS: () => DEFAULT_ENCRYPTION_FIELDS,
|
|
315
|
+
DbService: () => DbService,
|
|
316
|
+
TABLE_REGISTRY: () => TABLE_REGISTRY
|
|
317
|
+
});
|
|
318
|
+
var DEFAULT_ENCRYPTION_FIELDS, DEFAULT_CACHE_TTL_SECONDS, DEFAULT_AUDIT_RETENTION_DAYS, DEFAULT_POOL_SIZE, DEFAULT_CONFIG, TABLE_REGISTRY, DbService;
|
|
319
|
+
var init_DbService = __esm({
|
|
320
|
+
"src/services/DbService.ts"() {
|
|
321
|
+
init_CoreEventManager();
|
|
322
|
+
DEFAULT_ENCRYPTION_FIELDS = {
|
|
323
|
+
// User PII
|
|
324
|
+
users: ["password_hash", "phone_number", "date_of_birth"],
|
|
325
|
+
// KYC sensitive data
|
|
326
|
+
"backoffice.kyc_submissions": ["tax_id", "address_line1", "address_line2"],
|
|
327
|
+
// Connected accounts OAuth tokens
|
|
328
|
+
connected_accounts: ["access_token_encrypted", "refresh_token_encrypted", "wallet_address"],
|
|
329
|
+
// Payment payout accounts
|
|
330
|
+
user_payout_accounts: ["provider_account_id", "account_holder_name"],
|
|
331
|
+
// Sessions
|
|
332
|
+
sessions: ["token"]
|
|
585
333
|
};
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
334
|
+
DEFAULT_CACHE_TTL_SECONDS = 300;
|
|
335
|
+
DEFAULT_AUDIT_RETENTION_DAYS = 180;
|
|
336
|
+
DEFAULT_POOL_SIZE = 10;
|
|
337
|
+
DEFAULT_CONFIG = {
|
|
338
|
+
adapter: "sql",
|
|
339
|
+
cache: {
|
|
340
|
+
enabled: true,
|
|
341
|
+
provider: "memory",
|
|
342
|
+
ttl: DEFAULT_CACHE_TTL_SECONDS
|
|
343
|
+
},
|
|
344
|
+
softDelete: {
|
|
345
|
+
enabled: true,
|
|
346
|
+
field: "deleted_at",
|
|
347
|
+
excludeTables: [
|
|
348
|
+
"audit_logs",
|
|
349
|
+
"audit.audit_logs",
|
|
350
|
+
"audit.feature_flag_evaluations",
|
|
351
|
+
"feature_flag_evaluations",
|
|
352
|
+
"notification_events",
|
|
353
|
+
"payments_activity",
|
|
354
|
+
"campaign_analytics",
|
|
355
|
+
"admin_actions",
|
|
356
|
+
"backoffice.admin_actions"
|
|
357
|
+
]
|
|
358
|
+
},
|
|
359
|
+
audit: {
|
|
360
|
+
enabled: true,
|
|
361
|
+
// Enabled by default for compliance
|
|
362
|
+
retentionDays: DEFAULT_AUDIT_RETENTION_DAYS,
|
|
363
|
+
excludeFields: ["password_hash", "api_key_hash", "token_hash"],
|
|
364
|
+
excludeTables: ["audit_logs", "audit.audit_logs"],
|
|
365
|
+
// Don't audit the audit table itself
|
|
366
|
+
schema: "audit",
|
|
367
|
+
// Use dedicated audit schema
|
|
368
|
+
usePartitionedTables: true
|
|
369
|
+
// Use daily partitioned tables (audit_log_yyyy_mm_dd)
|
|
370
|
+
}
|
|
371
|
+
// NOTE: Encryption requires key from environment or DbService.initialize()
|
|
372
|
+
// Enable via: DbService.initialize({ encryption: { enabled: true, key: process.env.ENCRYPTION_KEY!, fields: DEFAULT_ENCRYPTION_FIELDS } })
|
|
615
373
|
};
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
if (!connectionString) {
|
|
624
|
-
throw new errors.DatabasePackageError(
|
|
625
|
-
"SQL adapter requires a connection string. Provide `sql.connectionString` in config or use Core.initialize() with DATABASE_URL in env.",
|
|
626
|
-
errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
|
|
627
|
-
);
|
|
628
|
-
}
|
|
629
|
-
const tableIdColumns = this.buildTableIdColumns();
|
|
630
|
-
return {
|
|
631
|
-
connectionString,
|
|
632
|
-
dialect: config.sql?.dialect ?? "postgresql",
|
|
633
|
-
schema: config.sql?.schema,
|
|
634
|
-
// Pass through schema configuration
|
|
635
|
-
tableIdColumns
|
|
374
|
+
TABLE_REGISTRY = {
|
|
375
|
+
// Migration 008: Feature Flags (custom ID column 'key')
|
|
376
|
+
feature_flags: { idColumn: "key" },
|
|
377
|
+
// Test Feature Flags (custom ID column 'key')
|
|
378
|
+
test_feature_flags: { idColumn: "key" },
|
|
379
|
+
// Migration 010: Universal Analytics (custom ID column 'user_id')
|
|
380
|
+
user_analytics_summary: { idColumn: "user_id" }
|
|
636
381
|
};
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
382
|
+
DbService = class _DbService {
|
|
383
|
+
constructor() {
|
|
384
|
+
this.databaseService = null;
|
|
385
|
+
this.namedAdapters = /* @__PURE__ */ new Map();
|
|
386
|
+
this.config = null;
|
|
387
|
+
this.initialized = false;
|
|
388
|
+
}
|
|
389
|
+
static {
|
|
390
|
+
__name(this, "DbService");
|
|
391
|
+
}
|
|
392
|
+
static {
|
|
393
|
+
this.instance = null;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Emits a database error event via CoreEventManager.
|
|
397
|
+
* Called when database operations fail to integrate with global error handling.
|
|
398
|
+
*
|
|
399
|
+
* @param error - The error that occurred
|
|
400
|
+
* @param operation - The operation that failed (e.g., 'transaction', 'query', 'healthCheck')
|
|
401
|
+
* @param table - Optional table name involved in the operation
|
|
402
|
+
* @param query - Optional query string that failed
|
|
403
|
+
* @param recoverable - Whether the error is recoverable (default: false)
|
|
404
|
+
*/
|
|
405
|
+
emitDatabaseError(error, operation, options) {
|
|
406
|
+
const payload = {
|
|
407
|
+
error,
|
|
408
|
+
operation,
|
|
409
|
+
table: options?.table,
|
|
410
|
+
query: options?.query,
|
|
411
|
+
recoverable: options?.recoverable ?? false
|
|
412
|
+
};
|
|
413
|
+
exports.CoreEventManager.emit(core.CORE_EVENTS.DATABASE.ERROR, payload);
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Gets the singleton instance of DbService
|
|
417
|
+
*
|
|
418
|
+
* @returns {DbService} The singleton instance
|
|
419
|
+
*/
|
|
420
|
+
static getInstance() {
|
|
421
|
+
_DbService.instance ??= new _DbService();
|
|
422
|
+
return _DbService.instance;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Checks if the database service has been initialized
|
|
426
|
+
*
|
|
427
|
+
* @returns {boolean} True if initialized
|
|
428
|
+
*/
|
|
429
|
+
static isInitialized() {
|
|
430
|
+
return _DbService.instance?.initialized ?? false;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Resets the database service by closing connections and clearing the singleton instance
|
|
434
|
+
*
|
|
435
|
+
* @description Properly closes the database connection and clears the singleton.
|
|
436
|
+
* Useful for testing or when you need to reinitialize with different configuration.
|
|
437
|
+
*
|
|
438
|
+
* @example
|
|
439
|
+
* ```typescript
|
|
440
|
+
* await DbService.reset();
|
|
441
|
+
* await DbService.initialize({ adapter: 'sql', ... });
|
|
442
|
+
* ```
|
|
443
|
+
*/
|
|
444
|
+
static async reset() {
|
|
445
|
+
if (_DbService.instance) {
|
|
446
|
+
try {
|
|
447
|
+
await _DbService.instance.databaseService?.close?.();
|
|
448
|
+
} catch {
|
|
449
|
+
}
|
|
450
|
+
_DbService.instance = null;
|
|
655
451
|
}
|
|
656
452
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
453
|
+
/**
|
|
454
|
+
* Initializes the database connection
|
|
455
|
+
*
|
|
456
|
+
* @description Sets up the database connection using the provided configuration
|
|
457
|
+
* or environment variables. This method is idempotent - calling it multiple
|
|
458
|
+
* times won't create additional connections.
|
|
459
|
+
*
|
|
460
|
+
* **Environment Variables:**
|
|
461
|
+
* - `DATABASE_URL` - PostgreSQL connection string (for sql/drizzle adapters)
|
|
462
|
+
* - `ENCRYPTION_KEY` - 32-byte encryption key for field encryption (optional)
|
|
463
|
+
* - `SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`, `SUPABASE_ANON_PUBLIC_KEY` - For supabase adapter
|
|
464
|
+
*
|
|
465
|
+
* **Encryption:** If `ENCRYPTION_KEY` env is set, encryption is auto-enabled
|
|
466
|
+
* using `DEFAULT_ENCRYPTION_FIELDS`. Override via config.encryption.
|
|
467
|
+
*
|
|
468
|
+
* @param {DbServiceConfig} [config] - Optional configuration
|
|
469
|
+
* @returns {Promise<DbService>} The initialized DbService instance
|
|
470
|
+
* @throws {DatabasePackageError} When configuration is invalid or connection fails
|
|
471
|
+
*
|
|
472
|
+
* @example
|
|
473
|
+
* ```typescript
|
|
474
|
+
* // Minimal - uses DATABASE_URL env, auto-enables encryption if ENCRYPTION_KEY set
|
|
475
|
+
* await DbService.initialize();
|
|
476
|
+
*
|
|
477
|
+
* // With explicit encryption
|
|
478
|
+
* await DbService.initialize({
|
|
479
|
+
* adapter: 'sql',
|
|
480
|
+
* encryption: {
|
|
481
|
+
* enabled: true,
|
|
482
|
+
* key: 'your-32-byte-encryption-key-here!!',
|
|
483
|
+
* fields: DEFAULT_ENCRYPTION_FIELDS,
|
|
484
|
+
* },
|
|
485
|
+
* });
|
|
486
|
+
*
|
|
487
|
+
* // Or via Core.initialize() with env:
|
|
488
|
+
* await Core.initialize({
|
|
489
|
+
* env: { ENCRYPTION_KEY: '...' },
|
|
490
|
+
* db: { adapter: 'sql' },
|
|
491
|
+
* });
|
|
492
|
+
* ```
|
|
493
|
+
*/
|
|
494
|
+
/** Build encryption config from user config */
|
|
495
|
+
static buildEncryptionConfig(config) {
|
|
496
|
+
const encryption = config?.encryption;
|
|
497
|
+
if (!encryption?.key) return void 0;
|
|
498
|
+
return {
|
|
499
|
+
enabled: encryption.enabled ?? true,
|
|
500
|
+
key: encryption.key,
|
|
501
|
+
fields: encryption.fields ?? DEFAULT_ENCRYPTION_FIELDS,
|
|
502
|
+
algorithm: encryption.algorithm ?? "aes-256-gcm"
|
|
503
|
+
};
|
|
666
504
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
getDatabase(adapterName) {
|
|
678
|
-
if (adapterName) {
|
|
679
|
-
const namedAdapter = this.namedAdapters.get(adapterName);
|
|
680
|
-
if (!namedAdapter) {
|
|
681
|
-
throw new errors.DatabasePackageError(
|
|
682
|
-
`Named adapter '${adapterName}' not found. Available adapters: ${Array.from(this.namedAdapters.keys()).join(", ")}`,
|
|
683
|
-
errors$1.DATABASE_ERROR_CODES.INIT_FAILED
|
|
684
|
-
);
|
|
505
|
+
/** Merge user config with defaults */
|
|
506
|
+
static mergeConfig(config) {
|
|
507
|
+
return {
|
|
508
|
+
...DEFAULT_CONFIG,
|
|
509
|
+
...config,
|
|
510
|
+
softDelete: { ...DEFAULT_CONFIG.softDelete, ...config?.softDelete },
|
|
511
|
+
cache: { ...DEFAULT_CONFIG.cache, ...config?.cache },
|
|
512
|
+
audit: { ...DEFAULT_CONFIG.audit, ...config?.audit },
|
|
513
|
+
encryption: _DbService.buildEncryptionConfig(config)
|
|
514
|
+
};
|
|
685
515
|
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
)
|
|
736
|
-
|
|
737
|
-
throw error;
|
|
738
|
-
}
|
|
739
|
-
return result.value;
|
|
740
|
-
}
|
|
741
|
-
/**
|
|
742
|
-
* Sets audit context for subsequent operations
|
|
743
|
-
*
|
|
744
|
-
* @param context - Audit context (userId, requestId, etc.)
|
|
745
|
-
*/
|
|
746
|
-
async setAuditContext(context) {
|
|
747
|
-
const db = this.getDatabase();
|
|
748
|
-
await db.setAuditContext(context);
|
|
749
|
-
}
|
|
750
|
-
/**
|
|
751
|
-
* Performs a health check on the database connection
|
|
752
|
-
*
|
|
753
|
-
* @returns Health check result
|
|
754
|
-
*/
|
|
755
|
-
async healthCheck() {
|
|
756
|
-
try {
|
|
757
|
-
const db = this.getDatabase();
|
|
758
|
-
const result = await db.healthCheck();
|
|
759
|
-
if (result.success && result.value) {
|
|
516
|
+
/** Initialize named adapters */
|
|
517
|
+
async initializeNamedAdapters(mergedConfig) {
|
|
518
|
+
if (!mergedConfig.adapters) return;
|
|
519
|
+
for (const [name, adapterConfig] of Object.entries(mergedConfig.adapters)) {
|
|
520
|
+
const namedDbConfig = this.buildDatabaseConfig({
|
|
521
|
+
...mergedConfig,
|
|
522
|
+
adapter: adapterConfig.adapter,
|
|
523
|
+
drizzle: adapterConfig.drizzle,
|
|
524
|
+
supabase: adapterConfig.supabase,
|
|
525
|
+
sql: adapterConfig.sql
|
|
526
|
+
});
|
|
527
|
+
const namedService = await db.createDatabaseService(namedDbConfig);
|
|
528
|
+
this.namedAdapters.set(name, namedService);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
static async initialize(config) {
|
|
532
|
+
const instance = _DbService.getInstance();
|
|
533
|
+
if (instance.initialized) {
|
|
534
|
+
return instance;
|
|
535
|
+
}
|
|
536
|
+
const mergedConfig = _DbService.mergeConfig(config);
|
|
537
|
+
instance.config = mergedConfig;
|
|
538
|
+
const dbConfig = instance.buildDatabaseConfig(mergedConfig);
|
|
539
|
+
instance.databaseService = await db.createDatabaseService(dbConfig);
|
|
540
|
+
await instance.initializeNamedAdapters(mergedConfig);
|
|
541
|
+
instance.initialized = true;
|
|
542
|
+
return instance;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Builds the DatabaseServiceConfig based on the adapter type
|
|
546
|
+
* @private
|
|
547
|
+
*/
|
|
548
|
+
/** Builds adapter-specific config based on adapter type */
|
|
549
|
+
buildAdapterConfig(adapter, config) {
|
|
550
|
+
const builders = {
|
|
551
|
+
drizzle: /* @__PURE__ */ __name(() => this.buildDrizzleConfig(config), "drizzle"),
|
|
552
|
+
supabase: /* @__PURE__ */ __name(() => this.buildSupabaseConfig(config), "supabase"),
|
|
553
|
+
sql: /* @__PURE__ */ __name(() => this.buildSqlConfig(config), "sql")
|
|
554
|
+
};
|
|
555
|
+
const builder = builders[adapter];
|
|
556
|
+
if (!builder) {
|
|
557
|
+
throw new errors.DatabasePackageError(
|
|
558
|
+
`Unsupported adapter type: ${adapter}`,
|
|
559
|
+
errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
return builder();
|
|
563
|
+
}
|
|
564
|
+
/** Builds soft delete extension config */
|
|
565
|
+
buildSoftDeleteExtension(config) {
|
|
566
|
+
if (!config.softDelete?.enabled) return void 0;
|
|
760
567
|
return {
|
|
761
|
-
|
|
762
|
-
|
|
568
|
+
enabled: true,
|
|
569
|
+
field: config.softDelete.field ?? "deleted_at",
|
|
570
|
+
excludeTables: config.softDelete.excludeTables
|
|
763
571
|
};
|
|
764
572
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
573
|
+
/** Builds cache extension config */
|
|
574
|
+
buildCacheExtension(config) {
|
|
575
|
+
if (!config.cache?.enabled) return void 0;
|
|
576
|
+
return {
|
|
577
|
+
enabled: true,
|
|
578
|
+
ttl: config.cache.ttl ?? DEFAULT_CACHE_TTL_SECONDS,
|
|
579
|
+
provider: config.cache.provider ?? "memory",
|
|
580
|
+
invalidation: config.cache.invalidation ?? "write"
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
/** Builds audit extension config */
|
|
584
|
+
buildAuditExtension(config) {
|
|
585
|
+
if (!config.audit?.enabled) return void 0;
|
|
586
|
+
return {
|
|
587
|
+
enabled: true,
|
|
588
|
+
retentionDays: config.audit.retentionDays ?? DEFAULT_AUDIT_RETENTION_DAYS,
|
|
589
|
+
excludeFields: config.audit.excludeFields,
|
|
590
|
+
excludeTables: config.audit.excludeTables,
|
|
591
|
+
schema: config.audit.schema ?? "audit",
|
|
592
|
+
usePartitionedTables: config.audit.usePartitionedTables ?? true
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
/** Builds encryption extension config */
|
|
596
|
+
buildEncryptionExtension(config) {
|
|
597
|
+
if (!config.encryption?.enabled || !config.encryption.key) return void 0;
|
|
598
|
+
return {
|
|
599
|
+
enabled: true,
|
|
600
|
+
key: config.encryption.key,
|
|
601
|
+
fields: config.encryption.fields ?? {},
|
|
602
|
+
algorithm: config.encryption.algorithm
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
buildDatabaseConfig(config) {
|
|
606
|
+
const adapter = config.adapter ?? "drizzle";
|
|
607
|
+
const dbConfig = {
|
|
608
|
+
adapter,
|
|
609
|
+
config: this.buildAdapterConfig(adapter, config)
|
|
610
|
+
};
|
|
611
|
+
const softDelete = this.buildSoftDeleteExtension(config);
|
|
612
|
+
const cache = this.buildCacheExtension(config);
|
|
613
|
+
const audit = this.buildAuditExtension(config);
|
|
614
|
+
const encryption = this.buildEncryptionExtension(config);
|
|
615
|
+
if (softDelete) dbConfig.softDelete = softDelete;
|
|
616
|
+
if (cache) dbConfig.cache = cache;
|
|
617
|
+
if (audit) dbConfig.audit = audit;
|
|
618
|
+
if (encryption) dbConfig.encryption = encryption;
|
|
619
|
+
return dbConfig;
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Builds Drizzle adapter configuration
|
|
623
|
+
* @private
|
|
624
|
+
*/
|
|
625
|
+
buildDrizzleConfig(config) {
|
|
626
|
+
const connectionString = config.drizzle?.connectionString;
|
|
627
|
+
if (!connectionString) {
|
|
628
|
+
throw new errors.DatabasePackageError(
|
|
629
|
+
"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).",
|
|
630
|
+
errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
const tableIdColumns = this.buildTableIdColumns();
|
|
634
|
+
return {
|
|
635
|
+
connectionString,
|
|
636
|
+
poolSize: config.drizzle?.poolSize ?? DEFAULT_POOL_SIZE,
|
|
637
|
+
ssl: config.drizzle?.ssl,
|
|
638
|
+
schema: config.drizzle?.schema,
|
|
639
|
+
// Pass through schema configuration
|
|
640
|
+
tableIdColumns
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Builds Supabase adapter configuration
|
|
645
|
+
* @private
|
|
646
|
+
*/
|
|
647
|
+
// eslint-disable-next-line complexity
|
|
648
|
+
buildSupabaseConfig(config) {
|
|
649
|
+
const supabaseUrl = config.supabase?.supabaseUrl;
|
|
650
|
+
const supabaseServiceKey = config.supabase?.supabaseServiceKey;
|
|
651
|
+
const supabaseAnonKey = config.supabase?.supabaseAnonKey;
|
|
652
|
+
if (!supabaseUrl || !supabaseServiceKey) {
|
|
653
|
+
throw new errors.DatabasePackageError(
|
|
654
|
+
"Supabase adapter requires supabaseUrl and supabaseServiceKey. Provide in config or use Core.initialize() with SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY in env.",
|
|
655
|
+
errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
if (!supabaseAnonKey) {
|
|
659
|
+
throw new errors.DatabasePackageError(
|
|
660
|
+
"Supabase adapter requires supabaseAnonKey. Provide in config or use Core.initialize() with SUPABASE_ANON_PUBLIC_KEY in env.",
|
|
661
|
+
errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
const tableIdColumns = this.buildTableIdColumns();
|
|
665
|
+
return {
|
|
666
|
+
supabaseUrl,
|
|
667
|
+
supabaseServiceKey,
|
|
668
|
+
supabaseAnonKey,
|
|
669
|
+
schema: config.supabase?.schema ?? "public",
|
|
670
|
+
tableIdColumns
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Builds SQL adapter configuration
|
|
675
|
+
* @private
|
|
676
|
+
*/
|
|
677
|
+
buildSqlConfig(config) {
|
|
678
|
+
const connectionString = config.sql?.connectionString;
|
|
679
|
+
if (!connectionString) {
|
|
680
|
+
throw new errors.DatabasePackageError(
|
|
681
|
+
"SQL adapter requires a connection string. Provide `sql.connectionString` in config or use Core.initialize() with DATABASE_URL in env.",
|
|
682
|
+
errors$1.DATABASE_ERROR_CODES.CONFIG_REQUIRED
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
const tableIdColumns = this.buildTableIdColumns();
|
|
686
|
+
return {
|
|
687
|
+
connectionString,
|
|
688
|
+
dialect: config.sql?.dialect ?? "postgresql",
|
|
689
|
+
schema: config.sql?.schema,
|
|
690
|
+
// Pass through schema configuration
|
|
691
|
+
tableIdColumns
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Builds table ID column mappings from TABLE_REGISTRY
|
|
696
|
+
* @private
|
|
697
|
+
* @returns Record of table names to ID column names
|
|
698
|
+
*/
|
|
699
|
+
// eslint-disable-next-line complexity
|
|
700
|
+
buildTableIdColumns() {
|
|
701
|
+
const tableIdColumns = {};
|
|
702
|
+
const baseNameConflicts = /* @__PURE__ */ new Set();
|
|
703
|
+
const baseNameMappings = /* @__PURE__ */ new Map();
|
|
704
|
+
for (const tableName of Object.keys(TABLE_REGISTRY)) {
|
|
705
|
+
if (tableName.includes(".")) {
|
|
706
|
+
const baseName = tableName.split(".").pop();
|
|
707
|
+
if (baseNameMappings.has(baseName) || TABLE_REGISTRY[baseName]) {
|
|
708
|
+
baseNameConflicts.add(baseName);
|
|
709
|
+
} else {
|
|
710
|
+
baseNameMappings.set(baseName, tableName);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
for (const [tableName, config] of Object.entries(TABLE_REGISTRY)) {
|
|
715
|
+
const idColumn = config.idColumn ?? "id";
|
|
716
|
+
if (idColumn === "id") continue;
|
|
717
|
+
tableIdColumns[tableName] = idColumn;
|
|
718
|
+
if (!tableName.includes(".")) continue;
|
|
719
|
+
const baseName = tableName.split(".").pop();
|
|
720
|
+
if (!baseNameConflicts.has(baseName)) {
|
|
721
|
+
tableIdColumns[baseName] = idColumn;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return tableIdColumns;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Gets the initialized database service instance
|
|
728
|
+
*
|
|
729
|
+
* @param {string} [adapterName] - Optional named adapter to use instead of default
|
|
730
|
+
* @returns {DatabaseServiceInterface} The database service instance
|
|
731
|
+
* @throws {DatabasePackageError} When database is not initialized or named adapter not found
|
|
732
|
+
*/
|
|
733
|
+
getDatabase(adapterName) {
|
|
734
|
+
if (adapterName) {
|
|
735
|
+
const namedAdapter = this.namedAdapters.get(adapterName);
|
|
736
|
+
if (!namedAdapter) {
|
|
737
|
+
throw new errors.DatabasePackageError(
|
|
738
|
+
`Named adapter '${adapterName}' not found. Available adapters: ${Array.from(this.namedAdapters.keys()).join(", ")}`,
|
|
739
|
+
errors$1.DATABASE_ERROR_CODES.INIT_FAILED
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
return namedAdapter;
|
|
743
|
+
}
|
|
744
|
+
if (!this.databaseService) {
|
|
745
|
+
throw new errors.DatabasePackageError(
|
|
746
|
+
"Database not initialized. Call DbService.initialize() first.",
|
|
747
|
+
errors$1.DATABASE_ERROR_CODES.INIT_FAILED
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
return this.databaseService;
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Gets a named adapter by name
|
|
754
|
+
*
|
|
755
|
+
* @param {string} name - The name of the adapter
|
|
756
|
+
* @returns {DatabaseServiceInterface} The named adapter instance
|
|
757
|
+
* @throws {DatabasePackageError} When adapter not found
|
|
758
|
+
*/
|
|
759
|
+
getAdapter(name) {
|
|
760
|
+
return this.getDatabase(name);
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Lists all available named adapters
|
|
764
|
+
*
|
|
765
|
+
* @returns {string[]} Array of adapter names
|
|
766
|
+
*/
|
|
767
|
+
getAvailableAdapters() {
|
|
768
|
+
return ["default", ...Array.from(this.namedAdapters.keys())];
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Executes a database transaction with automatic rollback on failure
|
|
772
|
+
*
|
|
773
|
+
* @template T The return type of the transaction callback
|
|
774
|
+
* @param {Function} callback - Function that receives transaction object
|
|
775
|
+
* @returns {Promise<T>} The result of the transaction callback
|
|
776
|
+
* @throws {DatabasePackageError} When transaction fails
|
|
777
|
+
*/
|
|
778
|
+
async transaction(callback) {
|
|
779
|
+
const db = this.getDatabase();
|
|
780
|
+
const result = await db.transaction(callback);
|
|
781
|
+
if (!result.success) {
|
|
782
|
+
const errorMessage = result.error?.message ?? "Transaction failed";
|
|
783
|
+
const error = new errors.DatabasePackageError(errorMessage, errors$1.DATABASE_ERROR_CODES.TRANSACTION_FAILED);
|
|
784
|
+
this.emitDatabaseError(error, "transaction", { recoverable: true });
|
|
785
|
+
throw error;
|
|
786
|
+
}
|
|
787
|
+
if (result.value === void 0 || result.value === null) {
|
|
788
|
+
const error = new errors.DatabasePackageError(
|
|
789
|
+
"Transaction returned no value",
|
|
790
|
+
errors$1.DATABASE_ERROR_CODES.INVALID_RESULT
|
|
791
|
+
);
|
|
792
|
+
this.emitDatabaseError(error, "transaction", { recoverable: false });
|
|
793
|
+
throw error;
|
|
794
|
+
}
|
|
795
|
+
return result.value;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Sets audit context for subsequent operations
|
|
799
|
+
*
|
|
800
|
+
* @param context - Audit context (userId, requestId, etc.)
|
|
801
|
+
*/
|
|
802
|
+
async setAuditContext(context) {
|
|
803
|
+
const db = this.getDatabase();
|
|
804
|
+
await db.setAuditContext(context);
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Performs a health check on the database connection
|
|
808
|
+
*
|
|
809
|
+
* @returns Health check result
|
|
810
|
+
*/
|
|
811
|
+
async healthCheck() {
|
|
812
|
+
try {
|
|
813
|
+
const db = this.getDatabase();
|
|
814
|
+
const result = await db.healthCheck();
|
|
815
|
+
if (result.success && result.value) {
|
|
816
|
+
return {
|
|
817
|
+
isHealthy: result.value.isHealthy,
|
|
818
|
+
responseTime: result.value.responseTime
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
const errorMessage = result.error?.message ?? "Health check failed";
|
|
822
|
+
this.emitDatabaseError(
|
|
823
|
+
new errors.DatabasePackageError(errorMessage, errors$1.DATABASE_ERROR_CODES.CONNECTION_ERROR),
|
|
824
|
+
"healthCheck",
|
|
825
|
+
{ recoverable: true }
|
|
826
|
+
);
|
|
827
|
+
return {
|
|
828
|
+
isHealthy: false,
|
|
829
|
+
error: errorMessage
|
|
830
|
+
};
|
|
831
|
+
} catch (error) {
|
|
832
|
+
this.emitDatabaseError(error, "healthCheck", { recoverable: true });
|
|
833
|
+
return {
|
|
834
|
+
isHealthy: false,
|
|
835
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Gets the table registry with all known tables and their ID columns
|
|
841
|
+
*
|
|
842
|
+
* @returns The complete table registry
|
|
843
|
+
*/
|
|
844
|
+
static getTableRegistry() {
|
|
845
|
+
return TABLE_REGISTRY;
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Gets ID column for a specific table
|
|
849
|
+
*
|
|
850
|
+
* @param tableName - Name of the table
|
|
851
|
+
* @returns ID column name or 'id' as default
|
|
852
|
+
*/
|
|
853
|
+
static getTableIdColumn(tableName) {
|
|
854
|
+
return TABLE_REGISTRY[tableName]?.idColumn ?? "id";
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Reinitializes the database connection with new config
|
|
858
|
+
*
|
|
859
|
+
* @param {DbServiceConfig} [config] - New configuration
|
|
860
|
+
* @returns {Promise<DbService>} The reinitialized DbService instance
|
|
861
|
+
*/
|
|
862
|
+
static async reinitialize(config) {
|
|
863
|
+
const instance = _DbService.getInstance();
|
|
864
|
+
await instance.close();
|
|
865
|
+
instance.initialized = false;
|
|
866
|
+
return _DbService.initialize(config);
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Closes the database connection and cleans up resources
|
|
870
|
+
*/
|
|
871
|
+
async close() {
|
|
872
|
+
this.databaseService = null;
|
|
873
|
+
this.initialized = false;
|
|
874
|
+
this.config = null;
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Gets the current configuration
|
|
878
|
+
*
|
|
879
|
+
* @returns {DbServiceConfig | null} Current config or null if not initialized
|
|
880
|
+
*/
|
|
881
|
+
getConfig() {
|
|
882
|
+
return this.config;
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Gets the current adapter type
|
|
886
|
+
*
|
|
887
|
+
* @returns The adapter type or null if not initialized
|
|
888
|
+
*/
|
|
889
|
+
getAdapterType() {
|
|
890
|
+
return this.config?.adapter ?? null;
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Creates a dedicated database service instance (NOT the singleton)
|
|
894
|
+
*
|
|
895
|
+
* Use this when you need an isolated database connection with its own configuration
|
|
896
|
+
* that doesn't affect or get affected by the shared singleton instance.
|
|
897
|
+
*
|
|
898
|
+
* @param config - Database service configuration
|
|
899
|
+
* @returns Promise that resolves to a new dedicated DbService instance
|
|
900
|
+
*
|
|
901
|
+
* @example
|
|
902
|
+
* ```typescript
|
|
903
|
+
* // Create a dedicated instance for analytics database
|
|
904
|
+
* const analyticsDb = await DbService.createInstance({
|
|
905
|
+
* adapter: 'sql',
|
|
906
|
+
* sql: { connectionString: process.env.ANALYTICS_DB_URL },
|
|
907
|
+
* cache: { enabled: false }, // No caching for analytics
|
|
908
|
+
* });
|
|
909
|
+
*
|
|
910
|
+
* // This instance is independent from DbService.getInstance()
|
|
911
|
+
* const data = await analyticsDb.getDatabase().list('events');
|
|
912
|
+
*
|
|
913
|
+
* // Clean up when done
|
|
914
|
+
* await analyticsDb.close();
|
|
915
|
+
* ```
|
|
916
|
+
*/
|
|
917
|
+
// eslint-disable-next-line complexity
|
|
918
|
+
static async createInstance(config) {
|
|
919
|
+
const dedicatedInstance = new _DbService();
|
|
920
|
+
const encryptionKey = config.encryption?.key;
|
|
921
|
+
const encryptionConfig = encryptionKey ? {
|
|
922
|
+
enabled: config.encryption?.enabled ?? true,
|
|
923
|
+
key: encryptionKey,
|
|
924
|
+
fields: config.encryption?.fields ?? DEFAULT_ENCRYPTION_FIELDS,
|
|
925
|
+
algorithm: config.encryption?.algorithm ?? "aes-256-gcm"
|
|
926
|
+
} : void 0;
|
|
927
|
+
const mergedConfig = {
|
|
928
|
+
...DEFAULT_CONFIG,
|
|
929
|
+
...config,
|
|
930
|
+
// Deep merge extensions
|
|
931
|
+
softDelete: { ...DEFAULT_CONFIG.softDelete, ...config.softDelete },
|
|
932
|
+
cache: { ...DEFAULT_CONFIG.cache, ...config.cache },
|
|
933
|
+
audit: { ...DEFAULT_CONFIG.audit, ...config.audit },
|
|
934
|
+
// Encryption only if key is available
|
|
935
|
+
encryption: encryptionConfig
|
|
936
|
+
};
|
|
937
|
+
dedicatedInstance.config = mergedConfig;
|
|
938
|
+
const dbConfig = dedicatedInstance.buildDatabaseConfig(mergedConfig);
|
|
939
|
+
dedicatedInstance.databaseService = await db.createDatabaseService(dbConfig);
|
|
940
|
+
if (mergedConfig.adapters) {
|
|
941
|
+
for (const [name, adapterConfig] of Object.entries(mergedConfig.adapters)) {
|
|
942
|
+
const namedDbConfig = dedicatedInstance.buildDatabaseConfig({
|
|
943
|
+
...mergedConfig,
|
|
944
|
+
adapter: adapterConfig.adapter,
|
|
945
|
+
drizzle: adapterConfig.drizzle,
|
|
946
|
+
supabase: adapterConfig.supabase,
|
|
947
|
+
sql: adapterConfig.sql
|
|
948
|
+
});
|
|
949
|
+
const namedService = await db.createDatabaseService(namedDbConfig);
|
|
950
|
+
dedicatedInstance.namedAdapters.set(name, namedService);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
dedicatedInstance.initialized = true;
|
|
954
|
+
return dedicatedInstance;
|
|
955
|
+
}
|
|
956
|
+
};
|
|
835
957
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
//
|
|
879
|
-
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
// src/services/StorageService.ts
|
|
961
|
+
var StorageService_exports = {};
|
|
962
|
+
__export(StorageService_exports, {
|
|
963
|
+
StorageService: () => StorageService
|
|
964
|
+
});
|
|
965
|
+
var StorageService;
|
|
966
|
+
var init_StorageService = __esm({
|
|
967
|
+
"src/services/StorageService.ts"() {
|
|
968
|
+
init_CoreEventManager();
|
|
969
|
+
StorageService = class _StorageService {
|
|
970
|
+
constructor() {
|
|
971
|
+
this.storageService = null;
|
|
972
|
+
this.config = null;
|
|
973
|
+
this.initialized = false;
|
|
974
|
+
}
|
|
975
|
+
static {
|
|
976
|
+
__name(this, "StorageService");
|
|
977
|
+
}
|
|
978
|
+
static {
|
|
979
|
+
this.instance = null;
|
|
980
|
+
}
|
|
981
|
+
// ─────────────────────────────────────────────────────────────────
|
|
982
|
+
// Error Handling
|
|
983
|
+
// ─────────────────────────────────────────────────────────────────
|
|
984
|
+
/**
|
|
985
|
+
* Emits a storage error event via CoreEventManager.
|
|
986
|
+
* Called when storage operations fail to integrate with global error handling.
|
|
987
|
+
*/
|
|
988
|
+
emitStorageError(error, operation, options) {
|
|
989
|
+
const payload = {
|
|
990
|
+
error,
|
|
991
|
+
operation,
|
|
992
|
+
fileId: options?.fileId,
|
|
993
|
+
filename: options?.filename,
|
|
994
|
+
recoverable: options?.recoverable ?? false
|
|
995
|
+
};
|
|
996
|
+
exports.CoreEventManager.emit(core.CORE_EVENTS.STORAGE.ERROR, payload);
|
|
997
|
+
}
|
|
998
|
+
// ─────────────────────────────────────────────────────────────────
|
|
999
|
+
// Singleton Management
|
|
1000
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1001
|
+
/**
|
|
1002
|
+
* Gets the singleton instance of StorageService
|
|
1003
|
+
*/
|
|
1004
|
+
static getInstance() {
|
|
1005
|
+
_StorageService.instance ??= new _StorageService();
|
|
1006
|
+
return _StorageService.instance;
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Checks if the storage service has been initialized
|
|
1010
|
+
*/
|
|
1011
|
+
static isInitialized() {
|
|
1012
|
+
return _StorageService.instance?.initialized ?? false;
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Resets the storage service by clearing the singleton instance
|
|
1016
|
+
*/
|
|
1017
|
+
static async reset() {
|
|
1018
|
+
if (_StorageService.instance) {
|
|
1019
|
+
_StorageService.instance.storageService = null;
|
|
1020
|
+
_StorageService.instance.config = null;
|
|
1021
|
+
_StorageService.instance.initialized = false;
|
|
1022
|
+
_StorageService.instance = null;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Initializes the storage service
|
|
1027
|
+
*
|
|
1028
|
+
* @param config - Storage service configuration
|
|
1029
|
+
* @returns The initialized StorageService instance
|
|
1030
|
+
*/
|
|
1031
|
+
static async initialize(config) {
|
|
1032
|
+
const instance = _StorageService.getInstance();
|
|
1033
|
+
if (instance.initialized) {
|
|
1034
|
+
return instance;
|
|
1035
|
+
}
|
|
1036
|
+
instance.config = config;
|
|
1037
|
+
instance.storageService = new storage.StorageService(config);
|
|
1038
|
+
instance.initialized = true;
|
|
1039
|
+
return instance;
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Gets the raw underlying storage service instance without error handling wrapper.
|
|
1043
|
+
* Use this only if you need direct access to the underlying service.
|
|
1044
|
+
*
|
|
1045
|
+
* @returns The raw StorageService instance from @plyaz/storage
|
|
1046
|
+
* @throws {StoragePackageError} When storage is not initialized
|
|
1047
|
+
*/
|
|
1048
|
+
getRawStorage() {
|
|
1049
|
+
if (!this.storageService) {
|
|
1050
|
+
throw new errors.StoragePackageError(
|
|
1051
|
+
"Storage not initialized. Call StorageService.initialize() first or use Core.initialize() with storage config.",
|
|
1052
|
+
errors$1.STORAGE_ERROR_CODES.INITIALIZATION_FAILED
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
return this.storageService;
|
|
1056
|
+
}
|
|
1057
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1058
|
+
// Service Access
|
|
1059
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1060
|
+
/**
|
|
1061
|
+
* Gets the storage service with automatic error handling.
|
|
1062
|
+
* All method calls are wrapped with try/catch and emit error events on failure.
|
|
1063
|
+
* Any method added to @plyaz/storage will be automatically available.
|
|
1064
|
+
*
|
|
1065
|
+
* @example
|
|
1066
|
+
* ```typescript
|
|
1067
|
+
* const storage = StorageService.getInstance().getStorage();
|
|
1068
|
+
* await storage.uploadFile({ file, filename: 'doc.pdf' });
|
|
1069
|
+
* await storage.deleteFile({ fileId: '123' });
|
|
1070
|
+
* ```
|
|
1071
|
+
*
|
|
1072
|
+
* @returns StorageServiceImpl with automatic error handling
|
|
1073
|
+
*/
|
|
1074
|
+
getStorage() {
|
|
1075
|
+
const self = this;
|
|
1076
|
+
return new Proxy({}, {
|
|
1077
|
+
get(_, prop) {
|
|
1078
|
+
if (typeof prop === "symbol") {
|
|
1079
|
+
return void 0;
|
|
1080
|
+
}
|
|
1081
|
+
const storage = self.getRawStorage();
|
|
1082
|
+
const value = storage[prop];
|
|
1083
|
+
if (typeof value !== "function") {
|
|
1084
|
+
return value;
|
|
1085
|
+
}
|
|
1086
|
+
return (...args) => {
|
|
1087
|
+
try {
|
|
1088
|
+
const result = value.apply(storage, args);
|
|
1089
|
+
if (result instanceof Promise) {
|
|
1090
|
+
return result.catch((error) => {
|
|
1091
|
+
self.emitStorageError(error, prop, { recoverable: true });
|
|
1092
|
+
throw error;
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
return result;
|
|
1096
|
+
} catch (error) {
|
|
1097
|
+
self.emitStorageError(error, prop, { recoverable: true });
|
|
1098
|
+
throw error;
|
|
1099
|
+
}
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1105
|
+
// Health Check (special handling for response transformation)
|
|
1106
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1107
|
+
/**
|
|
1108
|
+
* Performs a health check on the storage service by checking all adapter health.
|
|
1109
|
+
* This method has special handling to transform the response format.
|
|
1110
|
+
*/
|
|
1111
|
+
async healthCheck() {
|
|
1112
|
+
const startTime = Date.now();
|
|
1113
|
+
try {
|
|
1114
|
+
const storage = this.getRawStorage();
|
|
1115
|
+
if (typeof storage.checkAllAdaptersHealth === "function") {
|
|
1116
|
+
await storage.checkAllAdaptersHealth();
|
|
1117
|
+
}
|
|
1118
|
+
const summary = typeof storage.getHealthSummary === "function" ? storage.getHealthSummary() : null;
|
|
1119
|
+
const responseTime = Date.now() - startTime;
|
|
1120
|
+
return {
|
|
1121
|
+
// Check if all adapters are healthy (healthy count equals total count)
|
|
1122
|
+
isHealthy: summary ? summary.healthy === summary.total : true,
|
|
1123
|
+
responseTime,
|
|
1124
|
+
error: void 0
|
|
1125
|
+
};
|
|
1126
|
+
} catch (error) {
|
|
1127
|
+
this.emitStorageError(error, "healthCheck", { recoverable: true });
|
|
1128
|
+
return {
|
|
1129
|
+
isHealthy: false,
|
|
1130
|
+
responseTime: Date.now() - startTime,
|
|
1131
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1136
|
+
// Lifecycle
|
|
1137
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1138
|
+
/**
|
|
1139
|
+
* Gets the current configuration
|
|
1140
|
+
*/
|
|
1141
|
+
getConfig() {
|
|
1142
|
+
return this.config;
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* Closes the storage service and cleans up resources
|
|
1146
|
+
*/
|
|
1147
|
+
async close() {
|
|
1148
|
+
if (this.storageService) {
|
|
1149
|
+
await this.storageService.destroy();
|
|
1150
|
+
}
|
|
1151
|
+
this.storageService = null;
|
|
1152
|
+
this.initialized = false;
|
|
1153
|
+
this.config = null;
|
|
1154
|
+
}
|
|
1155
|
+
/**
|
|
1156
|
+
* Creates a dedicated storage service instance (NOT the singleton)
|
|
1157
|
+
*
|
|
1158
|
+
* Use this when you need an isolated storage connection with its own configuration.
|
|
1159
|
+
*
|
|
1160
|
+
* @param config - Storage service configuration
|
|
1161
|
+
* @returns Promise that resolves to a new dedicated StorageService instance
|
|
1162
|
+
*/
|
|
1163
|
+
static async createInstance(config) {
|
|
1164
|
+
const dedicatedInstance = new _StorageService();
|
|
1165
|
+
dedicatedInstance.config = config;
|
|
1166
|
+
dedicatedInstance.storageService = new storage.StorageService(config);
|
|
1167
|
+
dedicatedInstance.initialized = true;
|
|
1168
|
+
return dedicatedInstance;
|
|
1169
|
+
}
|
|
880
1170
|
};
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1171
|
+
}
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
// src/services/NotificationService.ts
|
|
1175
|
+
var NotificationService_exports = {};
|
|
1176
|
+
__export(NotificationService_exports, {
|
|
1177
|
+
NotificationService: () => NotificationService
|
|
1178
|
+
});
|
|
1179
|
+
var NotificationService;
|
|
1180
|
+
var init_NotificationService = __esm({
|
|
1181
|
+
"src/services/NotificationService.ts"() {
|
|
1182
|
+
init_CoreEventManager();
|
|
1183
|
+
NotificationService = class _NotificationService {
|
|
1184
|
+
constructor() {
|
|
1185
|
+
this.notificationService = null;
|
|
1186
|
+
this.config = null;
|
|
1187
|
+
this.initialized = false;
|
|
1188
|
+
}
|
|
1189
|
+
static {
|
|
1190
|
+
__name(this, "NotificationService");
|
|
1191
|
+
}
|
|
1192
|
+
static {
|
|
1193
|
+
this.instance = null;
|
|
1194
|
+
}
|
|
1195
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1196
|
+
// Error Handling
|
|
1197
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1198
|
+
/**
|
|
1199
|
+
* Emits a notification error event via CoreEventManager.
|
|
1200
|
+
* Called when notification operations fail to integrate with global error handling.
|
|
1201
|
+
*/
|
|
1202
|
+
emitNotificationError(error, operation, options) {
|
|
1203
|
+
const payload = {
|
|
1204
|
+
error,
|
|
1205
|
+
operation,
|
|
1206
|
+
recipientId: options?.recipientId,
|
|
1207
|
+
channel: options?.channel,
|
|
1208
|
+
recoverable: options?.recoverable ?? false
|
|
1209
|
+
};
|
|
1210
|
+
exports.CoreEventManager.emit(core.CORE_EVENTS.NOTIFICATION.ERROR, payload);
|
|
1211
|
+
}
|
|
1212
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1213
|
+
// Singleton Management
|
|
1214
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1215
|
+
/**
|
|
1216
|
+
* Gets the singleton instance of NotificationService
|
|
1217
|
+
*/
|
|
1218
|
+
static getInstance() {
|
|
1219
|
+
_NotificationService.instance ??= new _NotificationService();
|
|
1220
|
+
return _NotificationService.instance;
|
|
1221
|
+
}
|
|
1222
|
+
/**
|
|
1223
|
+
* Checks if the notification service has been initialized
|
|
1224
|
+
*/
|
|
1225
|
+
static isInitialized() {
|
|
1226
|
+
return _NotificationService.instance?.initialized ?? false;
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Resets the notification service by clearing the singleton instance
|
|
1230
|
+
*/
|
|
1231
|
+
static async reset() {
|
|
1232
|
+
if (_NotificationService.instance) {
|
|
1233
|
+
_NotificationService.instance.notificationService = null;
|
|
1234
|
+
_NotificationService.instance.config = null;
|
|
1235
|
+
_NotificationService.instance.initialized = false;
|
|
1236
|
+
_NotificationService.instance = null;
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Initializes the notification service
|
|
1241
|
+
*
|
|
1242
|
+
* @param config - Notification service configuration
|
|
1243
|
+
* @returns The initialized NotificationService instance
|
|
1244
|
+
*/
|
|
1245
|
+
static async initialize(config) {
|
|
1246
|
+
const instance = _NotificationService.getInstance();
|
|
1247
|
+
if (instance.initialized) {
|
|
1248
|
+
return instance;
|
|
1249
|
+
}
|
|
1250
|
+
instance.config = config;
|
|
1251
|
+
instance.notificationService = new notifications.NotificationService(config);
|
|
1252
|
+
instance.initialized = true;
|
|
1253
|
+
return instance;
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Gets the raw underlying notification service instance without error handling wrapper.
|
|
1257
|
+
* Use this only if you need direct access to the underlying service.
|
|
1258
|
+
*
|
|
1259
|
+
* @returns The raw NotificationService instance from @plyaz/notifications
|
|
1260
|
+
* @throws {NotificationsPackageError} When notifications is not initialized
|
|
1261
|
+
*/
|
|
1262
|
+
getRawNotifications() {
|
|
1263
|
+
if (!this.notificationService) {
|
|
1264
|
+
throw new errors.NotificationPackageError(
|
|
1265
|
+
"Notifications not initialized. Call NotificationService.initialize() first or use Core.initialize() with notifications config.",
|
|
1266
|
+
errors$1.NOTIFICATION_ERROR_CODES.INITIALIZATION_FAILED
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
return this.notificationService;
|
|
1270
|
+
}
|
|
1271
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1272
|
+
// Service Access
|
|
1273
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1274
|
+
/**
|
|
1275
|
+
* Gets the notification service with automatic error handling.
|
|
1276
|
+
* All method calls are wrapped with try/catch and emit error events on failure.
|
|
1277
|
+
* Any method added to @plyaz/notifications will be automatically available.
|
|
1278
|
+
*
|
|
1279
|
+
* @example
|
|
1280
|
+
* ```typescript
|
|
1281
|
+
* const notifications = NotificationService.getInstance().getNotifications();
|
|
1282
|
+
* await notifications.sendEmail({ to: 'user@example.com', templateId: 'welcome' });
|
|
1283
|
+
* await notifications.sendSMS({ to: '+1234567890', message: 'Hello!' });
|
|
1284
|
+
* ```
|
|
1285
|
+
*
|
|
1286
|
+
* @returns NotificationServiceImpl with automatic error handling
|
|
1287
|
+
*/
|
|
1288
|
+
getNotifications() {
|
|
1289
|
+
const self = this;
|
|
1290
|
+
return new Proxy({}, {
|
|
1291
|
+
get(_, prop) {
|
|
1292
|
+
if (typeof prop === "symbol") {
|
|
1293
|
+
return void 0;
|
|
1294
|
+
}
|
|
1295
|
+
const notifications = self.getRawNotifications();
|
|
1296
|
+
const value = notifications[prop];
|
|
1297
|
+
if (typeof value !== "function") {
|
|
1298
|
+
return value;
|
|
1299
|
+
}
|
|
1300
|
+
return (...args) => {
|
|
1301
|
+
try {
|
|
1302
|
+
const result = value.apply(notifications, args);
|
|
1303
|
+
if (result instanceof Promise) {
|
|
1304
|
+
return result.catch((error) => {
|
|
1305
|
+
self.emitNotificationError(error, prop, { recoverable: true });
|
|
1306
|
+
throw error;
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
return result;
|
|
1310
|
+
} catch (error) {
|
|
1311
|
+
self.emitNotificationError(error, prop, { recoverable: true });
|
|
1312
|
+
throw error;
|
|
1313
|
+
}
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
892
1316
|
});
|
|
893
|
-
const namedService = await db.createDatabaseService(namedDbConfig);
|
|
894
|
-
dedicatedInstance.namedAdapters.set(name, namedService);
|
|
895
1317
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1318
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1319
|
+
// Health Check (special handling for response transformation)
|
|
1320
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1321
|
+
/**
|
|
1322
|
+
* Performs a health check on the notification service.
|
|
1323
|
+
* This method has special handling to transform the response format.
|
|
1324
|
+
*/
|
|
1325
|
+
async healthCheck() {
|
|
1326
|
+
try {
|
|
1327
|
+
const notifications = this.getRawNotifications();
|
|
1328
|
+
const result = await notifications.healthCheck();
|
|
1329
|
+
return {
|
|
1330
|
+
isHealthy: result.healthy,
|
|
1331
|
+
providers: result.providers,
|
|
1332
|
+
// Use optional chaining since error may not exist on all health check results
|
|
1333
|
+
error: "error" in result ? result.error : void 0
|
|
1334
|
+
};
|
|
1335
|
+
} catch (error) {
|
|
1336
|
+
this.emitNotificationError(error, "healthCheck", { recoverable: true });
|
|
1337
|
+
return {
|
|
1338
|
+
isHealthy: false,
|
|
1339
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
1340
|
+
};
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1344
|
+
// Lifecycle
|
|
1345
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1346
|
+
/**
|
|
1347
|
+
* Gets the current configuration
|
|
1348
|
+
*/
|
|
1349
|
+
getConfig() {
|
|
1350
|
+
return this.config;
|
|
1351
|
+
}
|
|
1352
|
+
/**
|
|
1353
|
+
* Closes the notification service and cleans up resources
|
|
1354
|
+
*/
|
|
1355
|
+
async close() {
|
|
1356
|
+
this.notificationService = null;
|
|
1357
|
+
this.initialized = false;
|
|
1358
|
+
this.config = null;
|
|
1359
|
+
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Creates a dedicated notification service instance (NOT the singleton)
|
|
1362
|
+
*
|
|
1363
|
+
* Use this when you need an isolated notification service with its own configuration.
|
|
1364
|
+
*
|
|
1365
|
+
* @param config - Notification service configuration
|
|
1366
|
+
* @returns Promise that resolves to a new dedicated NotificationService instance
|
|
1367
|
+
*/
|
|
1368
|
+
static async createInstance(config) {
|
|
1369
|
+
const dedicatedInstance = new _NotificationService();
|
|
1370
|
+
dedicatedInstance.config = config;
|
|
1371
|
+
dedicatedInstance.notificationService = new notifications.NotificationService(config);
|
|
1372
|
+
dedicatedInstance.initialized = true;
|
|
1373
|
+
return dedicatedInstance;
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
899
1376
|
}
|
|
900
|
-
};
|
|
1377
|
+
});
|
|
1378
|
+
|
|
1379
|
+
// src/init/CoreInitializer.ts
|
|
1380
|
+
init_DbService();
|
|
1381
|
+
|
|
1382
|
+
// src/services/ApiClientService.ts
|
|
1383
|
+
init_CoreEventManager();
|
|
901
1384
|
var MIN_RETRY_ATTEMPTS_PRODUCTION = 3;
|
|
902
1385
|
function getConfigForEnvironment(env) {
|
|
903
1386
|
switch (env) {
|
|
@@ -1112,7 +1595,7 @@ var ApiClientService = class _ApiClientService {
|
|
|
1112
1595
|
requestId
|
|
1113
1596
|
}
|
|
1114
1597
|
};
|
|
1115
|
-
CoreEventManager.emit(types.CORE_EVENTS.SYSTEM.ERROR, { errors: [serializedError] });
|
|
1598
|
+
exports.CoreEventManager.emit(types.CORE_EVENTS.SYSTEM.ERROR, { errors: [serializedError] });
|
|
1116
1599
|
} else {
|
|
1117
1600
|
const serializedErrors = errorDetails.map(
|
|
1118
1601
|
(detail, index) => ({
|
|
@@ -1136,7 +1619,7 @@ var ApiClientService = class _ApiClientService {
|
|
|
1136
1619
|
}
|
|
1137
1620
|
})
|
|
1138
1621
|
);
|
|
1139
|
-
CoreEventManager.emit(types.CORE_EVENTS.SYSTEM.ERROR, { errors: serializedErrors });
|
|
1622
|
+
exports.CoreEventManager.emit(types.CORE_EVENTS.SYSTEM.ERROR, { errors: serializedErrors });
|
|
1140
1623
|
}
|
|
1141
1624
|
} catch (e) {
|
|
1142
1625
|
console.error("[ApiClientService] Failed to emit error event:", e);
|
|
@@ -1289,7 +1772,7 @@ var ApiClientService = class _ApiClientService {
|
|
|
1289
1772
|
error,
|
|
1290
1773
|
duration: options.duration
|
|
1291
1774
|
};
|
|
1292
|
-
CoreEventManager.emit(types.CORE_EVENTS.API.REQUEST_ERROR, payload);
|
|
1775
|
+
exports.CoreEventManager.emit(types.CORE_EVENTS.API.REQUEST_ERROR, payload);
|
|
1293
1776
|
if (options.rethrow && error instanceof Error) {
|
|
1294
1777
|
throw error;
|
|
1295
1778
|
}
|
|
@@ -1967,582 +2450,190 @@ var CacheManager = class {
|
|
|
1967
2450
|
}
|
|
1968
2451
|
}
|
|
1969
2452
|
/**
|
|
1970
|
-
* Disposes of the cache manager and cleans up resources.
|
|
1971
|
-
*
|
|
1972
|
-
* @returns Promise that resolves when cleanup is complete
|
|
1973
|
-
*/
|
|
1974
|
-
async dispose() {
|
|
1975
|
-
await this.strategy.dispose?.();
|
|
1976
|
-
}
|
|
1977
|
-
};
|
|
1978
|
-
var CacheService = class _CacheService {
|
|
1979
|
-
/** Private constructor to enforce singleton */
|
|
1980
|
-
constructor() {
|
|
1981
|
-
/** Cache manager instance */
|
|
1982
|
-
this.cacheManager = null;
|
|
1983
|
-
/** Configuration used to initialize cache */
|
|
1984
|
-
this.config = null;
|
|
1985
|
-
/** Logger instance */
|
|
1986
|
-
this.logger = new logger$1.CoreLogger({ service: "CacheService" });
|
|
1987
|
-
}
|
|
1988
|
-
static {
|
|
1989
|
-
__name(this, "CacheService");
|
|
1990
|
-
}
|
|
1991
|
-
static {
|
|
1992
|
-
/** Singleton instance */
|
|
1993
|
-
this.instance = null;
|
|
1994
|
-
}
|
|
1995
|
-
/**
|
|
1996
|
-
* Initialize the cache service with configuration
|
|
1997
|
-
*
|
|
1998
|
-
* @param config - Cache configuration
|
|
1999
|
-
* @throws {CorePackageError} If already initialized or config is invalid
|
|
2000
|
-
*
|
|
2001
|
-
* @example
|
|
2002
|
-
* ```typescript
|
|
2003
|
-
* await CacheService.initialize({
|
|
2004
|
-
* strategy: 'redis',
|
|
2005
|
-
* isEnabled: true,
|
|
2006
|
-
* ttl: 300,
|
|
2007
|
-
* prefix: 'app',
|
|
2008
|
-
* redis: { url: process.env.REDIS_URL },
|
|
2009
|
-
* });
|
|
2010
|
-
* ```
|
|
2011
|
-
*/
|
|
2012
|
-
static async initialize(config$1) {
|
|
2013
|
-
if (this.instance?.cacheManager) {
|
|
2014
|
-
throw new errors.CorePackageError(
|
|
2015
|
-
"CacheService is already initialized. Call reset() first if you need to reinitialize.",
|
|
2016
|
-
errors$1.ERROR_CODES.VALIDATION_ERROR
|
|
2017
|
-
);
|
|
2018
|
-
}
|
|
2019
|
-
const service = this.instance ?? new _CacheService();
|
|
2020
|
-
this.instance = service;
|
|
2021
|
-
try {
|
|
2022
|
-
service.logger.info("[CacheService] Initializing cache...", {
|
|
2023
|
-
strategy: config$1.strategy,
|
|
2024
|
-
isEnabled: config$1.isEnabled,
|
|
2025
|
-
ttl: config$1.ttl
|
|
2026
|
-
});
|
|
2027
|
-
service.validateConfig(config$1);
|
|
2028
|
-
const cacheConfig = {
|
|
2029
|
-
...config$1,
|
|
2030
|
-
ttl: config$1.ttl ?? config.TIME_CONSTANTS.DEFAULT_CACHE_TTL
|
|
2031
|
-
};
|
|
2032
|
-
service.cacheManager = new CacheManager(cacheConfig);
|
|
2033
|
-
service.config = config$1;
|
|
2034
|
-
service.logger.info("[CacheService] Cache initialized successfully", {
|
|
2035
|
-
strategy: config$1.strategy
|
|
2036
|
-
});
|
|
2037
|
-
} catch (error) {
|
|
2038
|
-
service.logger.error("[CacheService] Failed to initialize cache", { error });
|
|
2039
|
-
throw new errors.CorePackageError(
|
|
2040
|
-
`Failed to initialize cache: ${error instanceof Error ? error.message : String(error)}`,
|
|
2041
|
-
errors$1.ERROR_CODES.VALIDATION_ERROR,
|
|
2042
|
-
{ cause: error instanceof Error ? error : void 0 }
|
|
2043
|
-
);
|
|
2044
|
-
}
|
|
2045
|
-
}
|
|
2046
|
-
/**
|
|
2047
|
-
* Get the singleton instance
|
|
2048
|
-
*
|
|
2049
|
-
* @throws {CorePackageError} If not initialized
|
|
2050
|
-
* @returns CacheService singleton instance
|
|
2051
|
-
*
|
|
2052
|
-
* @example
|
|
2053
|
-
* ```typescript
|
|
2054
|
-
* const cacheService = CacheService.getInstance();
|
|
2055
|
-
* const cache = cacheService.getCacheManager();
|
|
2056
|
-
* ```
|
|
2057
|
-
*/
|
|
2058
|
-
static getInstance() {
|
|
2059
|
-
if (!this.instance) {
|
|
2060
|
-
throw new errors.CorePackageError(
|
|
2061
|
-
"CacheService not initialized. Call CacheService.initialize() first.",
|
|
2062
|
-
errors$1.ERROR_CODES.CLIENT_INITIALIZATION_FAILED
|
|
2063
|
-
);
|
|
2064
|
-
}
|
|
2065
|
-
return this.instance;
|
|
2066
|
-
}
|
|
2067
|
-
/**
|
|
2068
|
-
* Get the cache manager instance
|
|
2069
|
-
*
|
|
2070
|
-
* @throws {CorePackageError} If cache not initialized
|
|
2071
|
-
* @returns CacheManager instance
|
|
2072
|
-
*
|
|
2073
|
-
* @example
|
|
2074
|
-
* ```typescript
|
|
2075
|
-
* const cache = CacheService.getInstance().getCacheManager();
|
|
2076
|
-
* await cache.set('user:123', userData);
|
|
2077
|
-
* const user = await cache.get('user:123');
|
|
2078
|
-
* ```
|
|
2079
|
-
*/
|
|
2080
|
-
getCacheManager() {
|
|
2081
|
-
if (!this.cacheManager) {
|
|
2082
|
-
throw new errors.CorePackageError(
|
|
2083
|
-
"Cache manager not initialized. Call CacheService.initialize() first.",
|
|
2084
|
-
errors$1.ERROR_CODES.CLIENT_INITIALIZATION_FAILED
|
|
2085
|
-
);
|
|
2086
|
-
}
|
|
2087
|
-
return this.cacheManager;
|
|
2088
|
-
}
|
|
2089
|
-
/**
|
|
2090
|
-
* Get current cache configuration
|
|
2091
|
-
*
|
|
2092
|
-
* @returns Current cache configuration or null if not initialized
|
|
2093
|
-
*/
|
|
2094
|
-
getConfig() {
|
|
2095
|
-
return this.config;
|
|
2096
|
-
}
|
|
2097
|
-
/**
|
|
2098
|
-
* Check if cache is initialized and enabled
|
|
2099
|
-
*
|
|
2100
|
-
* @returns True if cache is ready to use
|
|
2101
|
-
*/
|
|
2102
|
-
isInitialized() {
|
|
2103
|
-
return this.cacheManager !== null && (this.config?.isEnabled ?? false);
|
|
2104
|
-
}
|
|
2105
|
-
/**
|
|
2106
|
-
* Reset the cache service (useful for testing)
|
|
2107
|
-
*
|
|
2108
|
-
* @internal
|
|
2109
|
-
*/
|
|
2110
|
-
static reset() {
|
|
2111
|
-
if (this.instance?.cacheManager) {
|
|
2112
|
-
this.instance.logger.info("[CacheService] Resetting cache service");
|
|
2113
|
-
this.instance.cacheManager.clear().catch((error) => {
|
|
2114
|
-
this.instance?.logger.error("[CacheService] Error clearing cache during reset", { error });
|
|
2115
|
-
});
|
|
2116
|
-
}
|
|
2117
|
-
this.instance = null;
|
|
2118
|
-
}
|
|
2119
|
-
/**
|
|
2120
|
-
* Validate cache configuration
|
|
2121
|
-
*
|
|
2122
|
-
* @param config - Configuration to validate
|
|
2123
|
-
* @throws {CorePackageError} If configuration is invalid
|
|
2124
|
-
* @private
|
|
2125
|
-
*/
|
|
2126
|
-
// eslint-disable-next-line complexity
|
|
2127
|
-
validateConfig(config) {
|
|
2128
|
-
if (!features.CACHE_STRATEGIES.includes(config.strategy)) {
|
|
2129
|
-
throw new errors.CorePackageError(
|
|
2130
|
-
`Invalid cache strategy: ${config.strategy}. Must be one of: ${features.CACHE_STRATEGIES.join(", ")}`,
|
|
2131
|
-
errors$1.ERROR_CODES.VALIDATION_ERROR
|
|
2132
|
-
);
|
|
2133
|
-
}
|
|
2134
|
-
if (config.ttl !== void 0 && (config.ttl < 0 || !Number.isFinite(config.ttl))) {
|
|
2135
|
-
throw new errors.CorePackageError(
|
|
2136
|
-
`Invalid TTL value: ${config.ttl}. Must be a positive number.`,
|
|
2137
|
-
errors$1.ERROR_CODES.VALIDATION_ERROR
|
|
2138
|
-
);
|
|
2139
|
-
}
|
|
2140
|
-
if (config.strategy === "redis") {
|
|
2141
|
-
if (!config.redis?.url && !config.redis?.host) {
|
|
2142
|
-
throw new errors.CorePackageError(
|
|
2143
|
-
"Redis strategy requires either redis.url or redis.host configuration",
|
|
2144
|
-
errors$1.ERROR_CODES.VALIDATION_ERROR
|
|
2145
|
-
);
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
2148
|
-
this.logger.debug("[CacheService] Configuration validated successfully");
|
|
2149
|
-
}
|
|
2150
|
-
};
|
|
2151
|
-
var StorageService = class _StorageService {
|
|
2152
|
-
constructor() {
|
|
2153
|
-
this.storageService = null;
|
|
2154
|
-
this.config = null;
|
|
2155
|
-
this.initialized = false;
|
|
2156
|
-
}
|
|
2157
|
-
static {
|
|
2158
|
-
__name(this, "StorageService");
|
|
2159
|
-
}
|
|
2160
|
-
static {
|
|
2161
|
-
this.instance = null;
|
|
2162
|
-
}
|
|
2163
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2164
|
-
// Error Handling
|
|
2165
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2166
|
-
/**
|
|
2167
|
-
* Emits a storage error event via CoreEventManager.
|
|
2168
|
-
* Called when storage operations fail to integrate with global error handling.
|
|
2169
|
-
*/
|
|
2170
|
-
emitStorageError(error, operation, options) {
|
|
2171
|
-
const payload = {
|
|
2172
|
-
error,
|
|
2173
|
-
operation,
|
|
2174
|
-
fileId: options?.fileId,
|
|
2175
|
-
filename: options?.filename,
|
|
2176
|
-
recoverable: options?.recoverable ?? false
|
|
2177
|
-
};
|
|
2178
|
-
CoreEventManager.emit(core.CORE_EVENTS.STORAGE.ERROR, payload);
|
|
2179
|
-
}
|
|
2180
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2181
|
-
// Singleton Management
|
|
2182
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2183
|
-
/**
|
|
2184
|
-
* Gets the singleton instance of StorageService
|
|
2185
|
-
*/
|
|
2186
|
-
static getInstance() {
|
|
2187
|
-
_StorageService.instance ??= new _StorageService();
|
|
2188
|
-
return _StorageService.instance;
|
|
2189
|
-
}
|
|
2190
|
-
/**
|
|
2191
|
-
* Checks if the storage service has been initialized
|
|
2192
|
-
*/
|
|
2193
|
-
static isInitialized() {
|
|
2194
|
-
return _StorageService.instance?.initialized ?? false;
|
|
2195
|
-
}
|
|
2196
|
-
/**
|
|
2197
|
-
* Resets the storage service by clearing the singleton instance
|
|
2198
|
-
*/
|
|
2199
|
-
static async reset() {
|
|
2200
|
-
if (_StorageService.instance) {
|
|
2201
|
-
_StorageService.instance.storageService = null;
|
|
2202
|
-
_StorageService.instance.config = null;
|
|
2203
|
-
_StorageService.instance.initialized = false;
|
|
2204
|
-
_StorageService.instance = null;
|
|
2205
|
-
}
|
|
2206
|
-
}
|
|
2207
|
-
/**
|
|
2208
|
-
* Initializes the storage service
|
|
2209
|
-
*
|
|
2210
|
-
* @param config - Storage service configuration
|
|
2211
|
-
* @returns The initialized StorageService instance
|
|
2212
|
-
*/
|
|
2213
|
-
static async initialize(config) {
|
|
2214
|
-
const instance = _StorageService.getInstance();
|
|
2215
|
-
if (instance.initialized) {
|
|
2216
|
-
return instance;
|
|
2217
|
-
}
|
|
2218
|
-
instance.config = config;
|
|
2219
|
-
instance.storageService = new storage.StorageService(config);
|
|
2220
|
-
instance.initialized = true;
|
|
2221
|
-
return instance;
|
|
2222
|
-
}
|
|
2223
|
-
/**
|
|
2224
|
-
* Gets the raw underlying storage service instance without error handling wrapper.
|
|
2225
|
-
* Use this only if you need direct access to the underlying service.
|
|
2226
|
-
*
|
|
2227
|
-
* @returns The raw StorageService instance from @plyaz/storage
|
|
2228
|
-
* @throws {StoragePackageError} When storage is not initialized
|
|
2229
|
-
*/
|
|
2230
|
-
getRawStorage() {
|
|
2231
|
-
if (!this.storageService) {
|
|
2232
|
-
throw new errors.StoragePackageError(
|
|
2233
|
-
"Storage not initialized. Call StorageService.initialize() first or use Core.initialize() with storage config.",
|
|
2234
|
-
errors$1.STORAGE_ERROR_CODES.INITIALIZATION_FAILED
|
|
2235
|
-
);
|
|
2236
|
-
}
|
|
2237
|
-
return this.storageService;
|
|
2238
|
-
}
|
|
2239
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2240
|
-
// Service Access
|
|
2241
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2242
|
-
/**
|
|
2243
|
-
* Gets the storage service with automatic error handling.
|
|
2244
|
-
* All method calls are wrapped with try/catch and emit error events on failure.
|
|
2245
|
-
* Any method added to @plyaz/storage will be automatically available.
|
|
2246
|
-
*
|
|
2247
|
-
* @example
|
|
2248
|
-
* ```typescript
|
|
2249
|
-
* const storage = StorageService.getInstance().getStorage();
|
|
2250
|
-
* await storage.uploadFile({ file, filename: 'doc.pdf' });
|
|
2251
|
-
* await storage.deleteFile({ fileId: '123' });
|
|
2252
|
-
* ```
|
|
2253
|
-
*
|
|
2254
|
-
* @returns StorageServiceImpl with automatic error handling
|
|
2255
|
-
*/
|
|
2256
|
-
getStorage() {
|
|
2257
|
-
const self = this;
|
|
2258
|
-
return new Proxy({}, {
|
|
2259
|
-
get(_, prop) {
|
|
2260
|
-
if (typeof prop === "symbol") {
|
|
2261
|
-
return void 0;
|
|
2262
|
-
}
|
|
2263
|
-
const storage = self.getRawStorage();
|
|
2264
|
-
const value = storage[prop];
|
|
2265
|
-
if (typeof value !== "function") {
|
|
2266
|
-
return value;
|
|
2267
|
-
}
|
|
2268
|
-
return (...args) => {
|
|
2269
|
-
try {
|
|
2270
|
-
const result = value.apply(storage, args);
|
|
2271
|
-
if (result instanceof Promise) {
|
|
2272
|
-
return result.catch((error) => {
|
|
2273
|
-
self.emitStorageError(error, prop, { recoverable: true });
|
|
2274
|
-
throw error;
|
|
2275
|
-
});
|
|
2276
|
-
}
|
|
2277
|
-
return result;
|
|
2278
|
-
} catch (error) {
|
|
2279
|
-
self.emitStorageError(error, prop, { recoverable: true });
|
|
2280
|
-
throw error;
|
|
2281
|
-
}
|
|
2282
|
-
};
|
|
2283
|
-
}
|
|
2284
|
-
});
|
|
2285
|
-
}
|
|
2286
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2287
|
-
// Health Check (special handling for response transformation)
|
|
2288
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2289
|
-
/**
|
|
2290
|
-
* Performs a health check on the storage service by checking all adapter health.
|
|
2291
|
-
* This method has special handling to transform the response format.
|
|
2292
|
-
*/
|
|
2293
|
-
async healthCheck() {
|
|
2294
|
-
const startTime = Date.now();
|
|
2295
|
-
try {
|
|
2296
|
-
const storage = this.getRawStorage();
|
|
2297
|
-
if (typeof storage.checkAllAdaptersHealth === "function") {
|
|
2298
|
-
await storage.checkAllAdaptersHealth();
|
|
2299
|
-
}
|
|
2300
|
-
const summary = typeof storage.getHealthSummary === "function" ? storage.getHealthSummary() : null;
|
|
2301
|
-
const responseTime = Date.now() - startTime;
|
|
2302
|
-
return {
|
|
2303
|
-
// Check if all adapters are healthy (healthy count equals total count)
|
|
2304
|
-
isHealthy: summary ? summary.healthy === summary.total : true,
|
|
2305
|
-
responseTime,
|
|
2306
|
-
error: void 0
|
|
2307
|
-
};
|
|
2308
|
-
} catch (error) {
|
|
2309
|
-
this.emitStorageError(error, "healthCheck", { recoverable: true });
|
|
2310
|
-
return {
|
|
2311
|
-
isHealthy: false,
|
|
2312
|
-
responseTime: Date.now() - startTime,
|
|
2313
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
2314
|
-
};
|
|
2315
|
-
}
|
|
2316
|
-
}
|
|
2317
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2318
|
-
// Lifecycle
|
|
2319
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2320
|
-
/**
|
|
2321
|
-
* Gets the current configuration
|
|
2322
|
-
*/
|
|
2323
|
-
getConfig() {
|
|
2324
|
-
return this.config;
|
|
2325
|
-
}
|
|
2326
|
-
/**
|
|
2327
|
-
* Closes the storage service and cleans up resources
|
|
2328
|
-
*/
|
|
2329
|
-
async close() {
|
|
2330
|
-
if (this.storageService) {
|
|
2331
|
-
await this.storageService.destroy();
|
|
2332
|
-
}
|
|
2333
|
-
this.storageService = null;
|
|
2334
|
-
this.initialized = false;
|
|
2335
|
-
this.config = null;
|
|
2336
|
-
}
|
|
2337
|
-
/**
|
|
2338
|
-
* Creates a dedicated storage service instance (NOT the singleton)
|
|
2339
|
-
*
|
|
2340
|
-
* Use this when you need an isolated storage connection with its own configuration.
|
|
2341
|
-
*
|
|
2342
|
-
* @param config - Storage service configuration
|
|
2343
|
-
* @returns Promise that resolves to a new dedicated StorageService instance
|
|
2344
|
-
*/
|
|
2345
|
-
static async createInstance(config) {
|
|
2346
|
-
const dedicatedInstance = new _StorageService();
|
|
2347
|
-
dedicatedInstance.config = config;
|
|
2348
|
-
dedicatedInstance.storageService = new storage.StorageService(config);
|
|
2349
|
-
dedicatedInstance.initialized = true;
|
|
2350
|
-
return dedicatedInstance;
|
|
2351
|
-
}
|
|
2352
|
-
};
|
|
2353
|
-
var NotificationService = class _NotificationService {
|
|
2354
|
-
constructor() {
|
|
2355
|
-
this.notificationService = null;
|
|
2356
|
-
this.config = null;
|
|
2357
|
-
this.initialized = false;
|
|
2358
|
-
}
|
|
2359
|
-
static {
|
|
2360
|
-
__name(this, "NotificationService");
|
|
2361
|
-
}
|
|
2362
|
-
static {
|
|
2363
|
-
this.instance = null;
|
|
2364
|
-
}
|
|
2365
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2366
|
-
// Error Handling
|
|
2367
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2368
|
-
/**
|
|
2369
|
-
* Emits a notification error event via CoreEventManager.
|
|
2370
|
-
* Called when notification operations fail to integrate with global error handling.
|
|
2371
|
-
*/
|
|
2372
|
-
emitNotificationError(error, operation, options) {
|
|
2373
|
-
const payload = {
|
|
2374
|
-
error,
|
|
2375
|
-
operation,
|
|
2376
|
-
recipientId: options?.recipientId,
|
|
2377
|
-
channel: options?.channel,
|
|
2378
|
-
recoverable: options?.recoverable ?? false
|
|
2379
|
-
};
|
|
2380
|
-
CoreEventManager.emit(core.CORE_EVENTS.NOTIFICATION.ERROR, payload);
|
|
2381
|
-
}
|
|
2382
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2383
|
-
// Singleton Management
|
|
2384
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2385
|
-
/**
|
|
2386
|
-
* Gets the singleton instance of NotificationService
|
|
2387
|
-
*/
|
|
2388
|
-
static getInstance() {
|
|
2389
|
-
_NotificationService.instance ??= new _NotificationService();
|
|
2390
|
-
return _NotificationService.instance;
|
|
2391
|
-
}
|
|
2392
|
-
/**
|
|
2393
|
-
* Checks if the notification service has been initialized
|
|
2394
|
-
*/
|
|
2395
|
-
static isInitialized() {
|
|
2396
|
-
return _NotificationService.instance?.initialized ?? false;
|
|
2397
|
-
}
|
|
2398
|
-
/**
|
|
2399
|
-
* Resets the notification service by clearing the singleton instance
|
|
2400
|
-
*/
|
|
2401
|
-
static async reset() {
|
|
2402
|
-
if (_NotificationService.instance) {
|
|
2403
|
-
_NotificationService.instance.notificationService = null;
|
|
2404
|
-
_NotificationService.instance.config = null;
|
|
2405
|
-
_NotificationService.instance.initialized = false;
|
|
2406
|
-
_NotificationService.instance = null;
|
|
2407
|
-
}
|
|
2408
|
-
}
|
|
2409
|
-
/**
|
|
2410
|
-
* Initializes the notification service
|
|
2453
|
+
* Disposes of the cache manager and cleans up resources.
|
|
2411
2454
|
*
|
|
2412
|
-
* @
|
|
2413
|
-
* @returns The initialized NotificationService instance
|
|
2455
|
+
* @returns Promise that resolves when cleanup is complete
|
|
2414
2456
|
*/
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
instance
|
|
2423
|
-
|
|
2457
|
+
async dispose() {
|
|
2458
|
+
await this.strategy.dispose?.();
|
|
2459
|
+
}
|
|
2460
|
+
};
|
|
2461
|
+
var CacheService = class _CacheService {
|
|
2462
|
+
/** Private constructor to enforce singleton */
|
|
2463
|
+
constructor() {
|
|
2464
|
+
/** Cache manager instance */
|
|
2465
|
+
this.cacheManager = null;
|
|
2466
|
+
/** Configuration used to initialize cache */
|
|
2467
|
+
this.config = null;
|
|
2468
|
+
/** Logger instance */
|
|
2469
|
+
this.logger = new logger$1.CoreLogger({ service: "CacheService" });
|
|
2470
|
+
}
|
|
2471
|
+
static {
|
|
2472
|
+
__name(this, "CacheService");
|
|
2473
|
+
}
|
|
2474
|
+
static {
|
|
2475
|
+
/** Singleton instance */
|
|
2476
|
+
this.instance = null;
|
|
2424
2477
|
}
|
|
2425
2478
|
/**
|
|
2426
|
-
*
|
|
2427
|
-
*
|
|
2479
|
+
* Initialize the cache service with configuration
|
|
2480
|
+
*
|
|
2481
|
+
* @param config - Cache configuration
|
|
2482
|
+
* @throws {CorePackageError} If already initialized or config is invalid
|
|
2428
2483
|
*
|
|
2429
|
-
* @
|
|
2430
|
-
*
|
|
2484
|
+
* @example
|
|
2485
|
+
* ```typescript
|
|
2486
|
+
* await CacheService.initialize({
|
|
2487
|
+
* strategy: 'redis',
|
|
2488
|
+
* isEnabled: true,
|
|
2489
|
+
* ttl: 300,
|
|
2490
|
+
* prefix: 'app',
|
|
2491
|
+
* redis: { url: process.env.REDIS_URL },
|
|
2492
|
+
* });
|
|
2493
|
+
* ```
|
|
2431
2494
|
*/
|
|
2432
|
-
|
|
2433
|
-
if (
|
|
2434
|
-
throw new errors.
|
|
2435
|
-
"
|
|
2436
|
-
errors$1.
|
|
2495
|
+
static async initialize(config$1) {
|
|
2496
|
+
if (this.instance?.cacheManager) {
|
|
2497
|
+
throw new errors.CorePackageError(
|
|
2498
|
+
"CacheService is already initialized. Call reset() first if you need to reinitialize.",
|
|
2499
|
+
errors$1.ERROR_CODES.VALIDATION_ERROR
|
|
2500
|
+
);
|
|
2501
|
+
}
|
|
2502
|
+
const service = this.instance ?? new _CacheService();
|
|
2503
|
+
this.instance = service;
|
|
2504
|
+
try {
|
|
2505
|
+
service.logger.info("[CacheService] Initializing cache...", {
|
|
2506
|
+
strategy: config$1.strategy,
|
|
2507
|
+
isEnabled: config$1.isEnabled,
|
|
2508
|
+
ttl: config$1.ttl
|
|
2509
|
+
});
|
|
2510
|
+
service.validateConfig(config$1);
|
|
2511
|
+
const cacheConfig = {
|
|
2512
|
+
...config$1,
|
|
2513
|
+
ttl: config$1.ttl ?? config.TIME_CONSTANTS.DEFAULT_CACHE_TTL
|
|
2514
|
+
};
|
|
2515
|
+
service.cacheManager = new CacheManager(cacheConfig);
|
|
2516
|
+
service.config = config$1;
|
|
2517
|
+
service.logger.info("[CacheService] Cache initialized successfully", {
|
|
2518
|
+
strategy: config$1.strategy
|
|
2519
|
+
});
|
|
2520
|
+
} catch (error) {
|
|
2521
|
+
service.logger.error("[CacheService] Failed to initialize cache", { error });
|
|
2522
|
+
throw new errors.CorePackageError(
|
|
2523
|
+
`Failed to initialize cache: ${error instanceof Error ? error.message : String(error)}`,
|
|
2524
|
+
errors$1.ERROR_CODES.VALIDATION_ERROR,
|
|
2525
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
2437
2526
|
);
|
|
2438
2527
|
}
|
|
2439
|
-
return this.notificationService;
|
|
2440
2528
|
}
|
|
2441
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2442
|
-
// Service Access
|
|
2443
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2444
2529
|
/**
|
|
2445
|
-
*
|
|
2446
|
-
*
|
|
2447
|
-
*
|
|
2530
|
+
* Get the singleton instance
|
|
2531
|
+
*
|
|
2532
|
+
* @throws {CorePackageError} If not initialized
|
|
2533
|
+
* @returns CacheService singleton instance
|
|
2448
2534
|
*
|
|
2449
2535
|
* @example
|
|
2450
2536
|
* ```typescript
|
|
2451
|
-
* const
|
|
2452
|
-
*
|
|
2453
|
-
* await notifications.sendSMS({ to: '+1234567890', message: 'Hello!' });
|
|
2537
|
+
* const cacheService = CacheService.getInstance();
|
|
2538
|
+
* const cache = cacheService.getCacheManager();
|
|
2454
2539
|
* ```
|
|
2455
|
-
*
|
|
2456
|
-
* @returns NotificationServiceImpl with automatic error handling
|
|
2457
2540
|
*/
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
const value = notifications[prop];
|
|
2467
|
-
if (typeof value !== "function") {
|
|
2468
|
-
return value;
|
|
2469
|
-
}
|
|
2470
|
-
return (...args) => {
|
|
2471
|
-
try {
|
|
2472
|
-
const result = value.apply(notifications, args);
|
|
2473
|
-
if (result instanceof Promise) {
|
|
2474
|
-
return result.catch((error) => {
|
|
2475
|
-
self.emitNotificationError(error, prop, { recoverable: true });
|
|
2476
|
-
throw error;
|
|
2477
|
-
});
|
|
2478
|
-
}
|
|
2479
|
-
return result;
|
|
2480
|
-
} catch (error) {
|
|
2481
|
-
self.emitNotificationError(error, prop, { recoverable: true });
|
|
2482
|
-
throw error;
|
|
2483
|
-
}
|
|
2484
|
-
};
|
|
2485
|
-
}
|
|
2486
|
-
});
|
|
2541
|
+
static getInstance() {
|
|
2542
|
+
if (!this.instance) {
|
|
2543
|
+
throw new errors.CorePackageError(
|
|
2544
|
+
"CacheService not initialized. Call CacheService.initialize() first.",
|
|
2545
|
+
errors$1.ERROR_CODES.CLIENT_INITIALIZATION_FAILED
|
|
2546
|
+
);
|
|
2547
|
+
}
|
|
2548
|
+
return this.instance;
|
|
2487
2549
|
}
|
|
2488
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2489
|
-
// Health Check (special handling for response transformation)
|
|
2490
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2491
2550
|
/**
|
|
2492
|
-
*
|
|
2493
|
-
*
|
|
2551
|
+
* Get the cache manager instance
|
|
2552
|
+
*
|
|
2553
|
+
* @throws {CorePackageError} If cache not initialized
|
|
2554
|
+
* @returns CacheManager instance
|
|
2555
|
+
*
|
|
2556
|
+
* @example
|
|
2557
|
+
* ```typescript
|
|
2558
|
+
* const cache = CacheService.getInstance().getCacheManager();
|
|
2559
|
+
* await cache.set('user:123', userData);
|
|
2560
|
+
* const user = await cache.get('user:123');
|
|
2561
|
+
* ```
|
|
2494
2562
|
*/
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
providers: result.providers,
|
|
2502
|
-
// Use optional chaining since error may not exist on all health check results
|
|
2503
|
-
error: "error" in result ? result.error : void 0
|
|
2504
|
-
};
|
|
2505
|
-
} catch (error) {
|
|
2506
|
-
this.emitNotificationError(error, "healthCheck", { recoverable: true });
|
|
2507
|
-
return {
|
|
2508
|
-
isHealthy: false,
|
|
2509
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
2510
|
-
};
|
|
2563
|
+
getCacheManager() {
|
|
2564
|
+
if (!this.cacheManager) {
|
|
2565
|
+
throw new errors.CorePackageError(
|
|
2566
|
+
"Cache manager not initialized. Call CacheService.initialize() first.",
|
|
2567
|
+
errors$1.ERROR_CODES.CLIENT_INITIALIZATION_FAILED
|
|
2568
|
+
);
|
|
2511
2569
|
}
|
|
2570
|
+
return this.cacheManager;
|
|
2512
2571
|
}
|
|
2513
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2514
|
-
// Lifecycle
|
|
2515
|
-
// ─────────────────────────────────────────────────────────────────
|
|
2516
2572
|
/**
|
|
2517
|
-
*
|
|
2573
|
+
* Get current cache configuration
|
|
2574
|
+
*
|
|
2575
|
+
* @returns Current cache configuration or null if not initialized
|
|
2518
2576
|
*/
|
|
2519
2577
|
getConfig() {
|
|
2520
2578
|
return this.config;
|
|
2521
2579
|
}
|
|
2522
2580
|
/**
|
|
2523
|
-
*
|
|
2581
|
+
* Check if cache is initialized and enabled
|
|
2582
|
+
*
|
|
2583
|
+
* @returns True if cache is ready to use
|
|
2524
2584
|
*/
|
|
2525
|
-
|
|
2526
|
-
this.
|
|
2527
|
-
this.initialized = false;
|
|
2528
|
-
this.config = null;
|
|
2585
|
+
isInitialized() {
|
|
2586
|
+
return this.cacheManager !== null && (this.config?.isEnabled ?? false);
|
|
2529
2587
|
}
|
|
2530
2588
|
/**
|
|
2531
|
-
*
|
|
2589
|
+
* Reset the cache service (useful for testing)
|
|
2532
2590
|
*
|
|
2533
|
-
*
|
|
2591
|
+
* @internal
|
|
2592
|
+
*/
|
|
2593
|
+
static reset() {
|
|
2594
|
+
if (this.instance?.cacheManager) {
|
|
2595
|
+
this.instance.logger.info("[CacheService] Resetting cache service");
|
|
2596
|
+
this.instance.cacheManager.clear().catch((error) => {
|
|
2597
|
+
this.instance?.logger.error("[CacheService] Error clearing cache during reset", { error });
|
|
2598
|
+
});
|
|
2599
|
+
}
|
|
2600
|
+
this.instance = null;
|
|
2601
|
+
}
|
|
2602
|
+
/**
|
|
2603
|
+
* Validate cache configuration
|
|
2534
2604
|
*
|
|
2535
|
-
* @param config -
|
|
2536
|
-
* @
|
|
2605
|
+
* @param config - Configuration to validate
|
|
2606
|
+
* @throws {CorePackageError} If configuration is invalid
|
|
2607
|
+
* @private
|
|
2537
2608
|
*/
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2609
|
+
// eslint-disable-next-line complexity
|
|
2610
|
+
validateConfig(config) {
|
|
2611
|
+
if (!features.CACHE_STRATEGIES.includes(config.strategy)) {
|
|
2612
|
+
throw new errors.CorePackageError(
|
|
2613
|
+
`Invalid cache strategy: ${config.strategy}. Must be one of: ${features.CACHE_STRATEGIES.join(", ")}`,
|
|
2614
|
+
errors$1.ERROR_CODES.VALIDATION_ERROR
|
|
2615
|
+
);
|
|
2616
|
+
}
|
|
2617
|
+
if (config.ttl !== void 0 && (config.ttl < 0 || !Number.isFinite(config.ttl))) {
|
|
2618
|
+
throw new errors.CorePackageError(
|
|
2619
|
+
`Invalid TTL value: ${config.ttl}. Must be a positive number.`,
|
|
2620
|
+
errors$1.ERROR_CODES.VALIDATION_ERROR
|
|
2621
|
+
);
|
|
2622
|
+
}
|
|
2623
|
+
if (config.strategy === "redis") {
|
|
2624
|
+
if (!config.redis?.url && !config.redis?.host) {
|
|
2625
|
+
throw new errors.CorePackageError(
|
|
2626
|
+
"Redis strategy requires either redis.url or redis.host configuration",
|
|
2627
|
+
errors$1.ERROR_CODES.VALIDATION_ERROR
|
|
2628
|
+
);
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
this.logger.debug("[CacheService] Configuration validated successfully");
|
|
2544
2632
|
}
|
|
2545
2633
|
};
|
|
2634
|
+
|
|
2635
|
+
// src/init/CoreInitializer.ts
|
|
2636
|
+
init_CoreEventManager();
|
|
2546
2637
|
var BaseDomainService = class {
|
|
2547
2638
|
// ─────────────────────────────────────────────────────────────────────────
|
|
2548
2639
|
// Constructor
|
|
@@ -2941,6 +3032,9 @@ var BaseDomainService = class {
|
|
|
2941
3032
|
);
|
|
2942
3033
|
}
|
|
2943
3034
|
};
|
|
3035
|
+
|
|
3036
|
+
// src/domain/base/BaseFrontendDomainService.ts
|
|
3037
|
+
init_CoreEventManager();
|
|
2944
3038
|
var BaseFrontendDomainService = class extends BaseDomainService {
|
|
2945
3039
|
// ─────────────────────────────────────────────────────────────────────────
|
|
2946
3040
|
// Constructor
|
|
@@ -3749,7 +3843,7 @@ var BaseFrontendDomainService = class extends BaseDomainService {
|
|
|
3749
3843
|
*/
|
|
3750
3844
|
emitEvent(event, payload) {
|
|
3751
3845
|
const eventName = `${this.eventPrefix}:${event}`;
|
|
3752
|
-
CoreEventManager.emit(eventName, payload);
|
|
3846
|
+
exports.CoreEventManager.emit(eventName, payload);
|
|
3753
3847
|
this.logDebug(`Event emitted: ${eventName}`, payload);
|
|
3754
3848
|
}
|
|
3755
3849
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -3838,6 +3932,9 @@ var BaseFrontendDomainService = class extends BaseDomainService {
|
|
|
3838
3932
|
);
|
|
3839
3933
|
}
|
|
3840
3934
|
};
|
|
3935
|
+
|
|
3936
|
+
// src/events/index.ts
|
|
3937
|
+
init_CoreEventManager();
|
|
3841
3938
|
var BaseBackendDomainService = class extends BaseDomainService {
|
|
3842
3939
|
static {
|
|
3843
3940
|
__name(this, "BaseBackendDomainService");
|
|
@@ -4775,7 +4872,7 @@ var BaseBackendDomainService = class extends BaseDomainService {
|
|
|
4775
4872
|
*/
|
|
4776
4873
|
emitEvent(event, payload) {
|
|
4777
4874
|
if (this.emitEvents) {
|
|
4778
|
-
CoreEventManager.emit(`${this.eventPrefix}:${event}`, payload);
|
|
4875
|
+
exports.CoreEventManager.emit(`${this.eventPrefix}:${event}`, payload);
|
|
4779
4876
|
}
|
|
4780
4877
|
}
|
|
4781
4878
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -5235,6 +5332,10 @@ function keysToSnake(obj) {
|
|
|
5235
5332
|
return result;
|
|
5236
5333
|
}
|
|
5237
5334
|
__name(keysToSnake, "keysToSnake");
|
|
5335
|
+
|
|
5336
|
+
// src/models/example/ExampleRepository.ts
|
|
5337
|
+
init_DbService();
|
|
5338
|
+
init_common();
|
|
5238
5339
|
var EXAMPLE_TABLE = "examples";
|
|
5239
5340
|
var DEFAULT_PAGINATION_LIMIT = 100;
|
|
5240
5341
|
var DUMMY_DATA = [
|
|
@@ -5412,31 +5513,99 @@ var ExampleRepository = class _ExampleRepository extends db.BaseRepository {
|
|
|
5412
5513
|
return super.delete(id, config);
|
|
5413
5514
|
}
|
|
5414
5515
|
// ─────────────────────────────────────────────────────────────────────────
|
|
5415
|
-
// Domain-Specific Query Methods
|
|
5516
|
+
// Domain-Specific Query Methods (using QueryBuilder fluent API)
|
|
5416
5517
|
// ─────────────────────────────────────────────────────────────────────────
|
|
5417
5518
|
/**
|
|
5418
|
-
* Find entities by status
|
|
5519
|
+
* Find entities by status using fluent QueryBuilder
|
|
5520
|
+
*
|
|
5521
|
+
* @example
|
|
5522
|
+
* ```typescript
|
|
5523
|
+
* const result = await repo.findByStatus('active');
|
|
5524
|
+
* ```
|
|
5419
5525
|
*/
|
|
5420
5526
|
async findByStatus(status) {
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5527
|
+
if (this.useDummyData) {
|
|
5528
|
+
return this.findMany({
|
|
5529
|
+
filter: { field: "status", operator: "eq", value: status }
|
|
5530
|
+
});
|
|
5531
|
+
}
|
|
5532
|
+
return this.query().where("status", "eq", status).execute();
|
|
5424
5533
|
}
|
|
5425
5534
|
/**
|
|
5426
|
-
* Find entities by owner
|
|
5535
|
+
* Find entities by owner using fluent QueryBuilder
|
|
5536
|
+
*
|
|
5537
|
+
* @example
|
|
5538
|
+
* ```typescript
|
|
5539
|
+
* const result = await repo.findByOwner('user-001');
|
|
5540
|
+
* ```
|
|
5427
5541
|
*/
|
|
5428
5542
|
async findByOwner(ownerId) {
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5543
|
+
if (this.useDummyData) {
|
|
5544
|
+
return this.findMany({
|
|
5545
|
+
filter: { field: "owner_id", operator: "eq", value: ownerId }
|
|
5546
|
+
});
|
|
5547
|
+
}
|
|
5548
|
+
return this.query().where("owner_id", "eq", ownerId).execute();
|
|
5432
5549
|
}
|
|
5433
5550
|
/**
|
|
5434
|
-
* Find visible entities only
|
|
5551
|
+
* Find visible entities only using fluent QueryBuilder
|
|
5552
|
+
*
|
|
5553
|
+
* @example
|
|
5554
|
+
* ```typescript
|
|
5555
|
+
* const result = await repo.findVisible();
|
|
5556
|
+
* ```
|
|
5435
5557
|
*/
|
|
5436
5558
|
async findVisible() {
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5559
|
+
if (this.useDummyData) {
|
|
5560
|
+
return this.findMany({
|
|
5561
|
+
filter: { field: "is_visible", operator: "eq", value: true }
|
|
5562
|
+
});
|
|
5563
|
+
}
|
|
5564
|
+
return this.query().where("is_visible", "eq", true).execute();
|
|
5565
|
+
}
|
|
5566
|
+
/**
|
|
5567
|
+
* Find active and visible entities using complex QueryBuilder
|
|
5568
|
+
*
|
|
5569
|
+
* @example
|
|
5570
|
+
* ```typescript
|
|
5571
|
+
* const items = await repo.findActiveAndVisible();
|
|
5572
|
+
* ```
|
|
5573
|
+
*/
|
|
5574
|
+
async findActiveAndVisible() {
|
|
5575
|
+
if (this.useDummyData) {
|
|
5576
|
+
return this.dummyStore.filter((r) => r.status === "active" && r.is_visible && !r.deleted_at);
|
|
5577
|
+
}
|
|
5578
|
+
return this.query().where("status", "eq", "active").andWhere("is_visible", "eq", true).whereNull("deleted_at").orderByDesc("created_at").getMany();
|
|
5579
|
+
}
|
|
5580
|
+
/**
|
|
5581
|
+
* Find entities by owner with status filter using QueryBuilder
|
|
5582
|
+
*
|
|
5583
|
+
* @example
|
|
5584
|
+
* ```typescript
|
|
5585
|
+
* const items = await repo.findByOwnerWithStatus('user-001', 'active');
|
|
5586
|
+
* ```
|
|
5587
|
+
*/
|
|
5588
|
+
async findByOwnerWithStatus(ownerId, status) {
|
|
5589
|
+
if (this.useDummyData) {
|
|
5590
|
+
return this.dummyStore.filter(
|
|
5591
|
+
(r) => r.owner_id === ownerId && r.status === status && !r.deleted_at
|
|
5592
|
+
);
|
|
5593
|
+
}
|
|
5594
|
+
return this.query().where("owner_id", "eq", ownerId).andWhere("status", "eq", status).orderByDesc("updated_at").getMany();
|
|
5595
|
+
}
|
|
5596
|
+
/**
|
|
5597
|
+
* Count entities by status using QueryBuilder
|
|
5598
|
+
*
|
|
5599
|
+
* @example
|
|
5600
|
+
* ```typescript
|
|
5601
|
+
* const count = await repo.countByStatus('active');
|
|
5602
|
+
* ```
|
|
5603
|
+
*/
|
|
5604
|
+
async countByStatus(status) {
|
|
5605
|
+
if (this.useDummyData) {
|
|
5606
|
+
return this.dummyStore.filter((r) => r.status === status && !r.deleted_at).length;
|
|
5607
|
+
}
|
|
5608
|
+
return this.query().where("status", "eq", status).count();
|
|
5440
5609
|
}
|
|
5441
5610
|
/**
|
|
5442
5611
|
* Check if entity exists by ID
|
|
@@ -5907,6 +6076,7 @@ var BackendExampleDomainService = class _BackendExampleDomainService extends Bas
|
|
|
5907
6076
|
// ─────────────────────────────────────────────────────────────────────────
|
|
5908
6077
|
};
|
|
5909
6078
|
new BackendExampleDomainService();
|
|
6079
|
+
init_CoreEventManager();
|
|
5910
6080
|
var DEFAULT_POLLING_INTERVAL_MS = 3e4;
|
|
5911
6081
|
var FrontendExampleDomainService = class _FrontendExampleDomainService extends BaseFrontendDomainService {
|
|
5912
6082
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -5956,6 +6126,7 @@ var FrontendExampleDomainService = class _FrontendExampleDomainService extends B
|
|
|
5956
6126
|
// store.setData(data);
|
|
5957
6127
|
// // Can also call domain-specific methods or other stores
|
|
5958
6128
|
// // store.selectItem(data.selectedId);
|
|
6129
|
+
// // store.selectItem(data.selectedId);
|
|
5959
6130
|
// },
|
|
5960
6131
|
// updateData: (store, data) => store.updateData(data),
|
|
5961
6132
|
// setLoading: (store, isLoading) => store.setLoading(isLoading),
|
|
@@ -6035,7 +6206,7 @@ var FrontendExampleDomainService = class _FrontendExampleDomainService extends B
|
|
|
6035
6206
|
* Note: Base class automatically calls syncToStores() which uses storeHandlers
|
|
6036
6207
|
*/
|
|
6037
6208
|
async afterFetchAll(entities) {
|
|
6038
|
-
CoreEventManager.emit("example:store:synced", { items: entities });
|
|
6209
|
+
exports.CoreEventManager.emit("example:store:synced", { items: entities });
|
|
6039
6210
|
}
|
|
6040
6211
|
// ─────────────────────────────────────────────────────────────────────────
|
|
6041
6212
|
// Optional: Manual Store Updates (Alternative to storeHandlers)
|
|
@@ -6074,7 +6245,7 @@ var FrontendExampleDomainService = class _FrontendExampleDomainService extends B
|
|
|
6074
6245
|
* ```
|
|
6075
6246
|
*/
|
|
6076
6247
|
on(event, handler) {
|
|
6077
|
-
return CoreEventManager.on(event, handler);
|
|
6248
|
+
return exports.CoreEventManager.on(event, handler);
|
|
6078
6249
|
}
|
|
6079
6250
|
// ─────────────────────────────────────────────────────────────────────────
|
|
6080
6251
|
// Polling (Domain-Specific Feature)
|
|
@@ -6171,6 +6342,9 @@ var FrontendExampleDomainService = class _FrontendExampleDomainService extends B
|
|
|
6171
6342
|
// All events are emitted automatically with 'example:' prefix!
|
|
6172
6343
|
// ─────────────────────────────────────────────────────────────────────────
|
|
6173
6344
|
};
|
|
6345
|
+
|
|
6346
|
+
// src/base/observability/BaseAdapter.ts
|
|
6347
|
+
init_common();
|
|
6174
6348
|
var BaseAdapter = class {
|
|
6175
6349
|
constructor() {
|
|
6176
6350
|
this._isInitialized = false;
|
|
@@ -6393,6 +6567,9 @@ var NoopAdapter = class extends BaseAdapter {
|
|
|
6393
6567
|
async doFlush() {
|
|
6394
6568
|
}
|
|
6395
6569
|
};
|
|
6570
|
+
|
|
6571
|
+
// src/base/observability/LoggerAdapter.ts
|
|
6572
|
+
init_common();
|
|
6396
6573
|
var LoggerAdapter = class extends BaseAdapter {
|
|
6397
6574
|
constructor() {
|
|
6398
6575
|
super(...arguments);
|
|
@@ -6542,6 +6719,12 @@ var LoggerAdapter = class extends BaseAdapter {
|
|
|
6542
6719
|
async doFlush() {
|
|
6543
6720
|
}
|
|
6544
6721
|
};
|
|
6722
|
+
|
|
6723
|
+
// src/base/observability/CompositeAdapter.ts
|
|
6724
|
+
init_common();
|
|
6725
|
+
|
|
6726
|
+
// src/base/observability/DatadogAdapter.ts
|
|
6727
|
+
init_common();
|
|
6545
6728
|
var DEFAULT_ADAPTER_TIMEOUT_MS = 5e3;
|
|
6546
6729
|
var ObservabilityService = class {
|
|
6547
6730
|
constructor() {
|
|
@@ -7294,10 +7477,11 @@ var ServiceRegistry = class _ServiceRegistry {
|
|
|
7294
7477
|
if (!dbConfig) {
|
|
7295
7478
|
return void 0;
|
|
7296
7479
|
}
|
|
7480
|
+
const { DbService: DbService2 } = await Promise.resolve().then(() => (init_DbService(), DbService_exports));
|
|
7297
7481
|
let dbInstance;
|
|
7298
7482
|
if (isDedicated && dbConfig) {
|
|
7299
7483
|
try {
|
|
7300
|
-
dbInstance = await
|
|
7484
|
+
dbInstance = await DbService2.createInstance(dbConfig);
|
|
7301
7485
|
_ServiceRegistry.logger.debug(
|
|
7302
7486
|
`Created dedicated database instance for service '${serviceKey}'`
|
|
7303
7487
|
);
|
|
@@ -7311,7 +7495,7 @@ var ServiceRegistry = class _ServiceRegistry {
|
|
|
7311
7495
|
}
|
|
7312
7496
|
} else {
|
|
7313
7497
|
try {
|
|
7314
|
-
dbInstance =
|
|
7498
|
+
dbInstance = DbService2.getInstance();
|
|
7315
7499
|
} catch {
|
|
7316
7500
|
_ServiceRegistry.logger.debug(
|
|
7317
7501
|
`Global DB not initialized, service '${serviceKey}' will not have DB`
|
|
@@ -7412,10 +7596,11 @@ var ServiceRegistry = class _ServiceRegistry {
|
|
|
7412
7596
|
if (!storageConfig) {
|
|
7413
7597
|
return void 0;
|
|
7414
7598
|
}
|
|
7599
|
+
const { StorageService: StorageService2 } = await Promise.resolve().then(() => (init_StorageService(), StorageService_exports));
|
|
7415
7600
|
let storageInstance;
|
|
7416
7601
|
if (isDedicated && storageConfig) {
|
|
7417
7602
|
try {
|
|
7418
|
-
storageInstance = await
|
|
7603
|
+
storageInstance = await StorageService2.createInstance(storageConfig);
|
|
7419
7604
|
_ServiceRegistry.logger.debug(
|
|
7420
7605
|
`Created dedicated storage instance for service '${serviceKey}'`
|
|
7421
7606
|
);
|
|
@@ -7427,7 +7612,7 @@ var ServiceRegistry = class _ServiceRegistry {
|
|
|
7427
7612
|
}
|
|
7428
7613
|
} else {
|
|
7429
7614
|
try {
|
|
7430
|
-
storageInstance =
|
|
7615
|
+
storageInstance = StorageService2.getInstance();
|
|
7431
7616
|
} catch {
|
|
7432
7617
|
_ServiceRegistry.logger.debug(
|
|
7433
7618
|
`Global storage not initialized, service '${serviceKey}' will not have storage`
|
|
@@ -7457,10 +7642,11 @@ var ServiceRegistry = class _ServiceRegistry {
|
|
|
7457
7642
|
if (!notificationsConfig) {
|
|
7458
7643
|
return void 0;
|
|
7459
7644
|
}
|
|
7645
|
+
const { NotificationService: NotificationService2 } = await Promise.resolve().then(() => (init_NotificationService(), NotificationService_exports));
|
|
7460
7646
|
let notificationsInstance;
|
|
7461
7647
|
if (isDedicated && notificationsConfig) {
|
|
7462
7648
|
try {
|
|
7463
|
-
notificationsInstance = await
|
|
7649
|
+
notificationsInstance = await NotificationService2.createInstance(notificationsConfig);
|
|
7464
7650
|
_ServiceRegistry.logger.debug(
|
|
7465
7651
|
`Created dedicated notifications instance for service '${serviceKey}'`
|
|
7466
7652
|
);
|
|
@@ -7472,7 +7658,7 @@ var ServiceRegistry = class _ServiceRegistry {
|
|
|
7472
7658
|
}
|
|
7473
7659
|
} else {
|
|
7474
7660
|
try {
|
|
7475
|
-
notificationsInstance =
|
|
7661
|
+
notificationsInstance = NotificationService2.getInstance();
|
|
7476
7662
|
} catch {
|
|
7477
7663
|
_ServiceRegistry.logger.debug(
|
|
7478
7664
|
`Global notifications not initialized, service '${serviceKey}' will not have notifications`
|
|
@@ -8050,8 +8236,9 @@ var Core = class _Core {
|
|
|
8050
8236
|
await _Core.initService(
|
|
8051
8237
|
"storage",
|
|
8052
8238
|
async () => {
|
|
8053
|
-
await
|
|
8054
|
-
|
|
8239
|
+
const { StorageService: StorageService2 } = await Promise.resolve().then(() => (init_StorageService(), StorageService_exports));
|
|
8240
|
+
await StorageService2.initialize(storageConfig);
|
|
8241
|
+
_Core._coreServices.storage = StorageService2.getInstance();
|
|
8055
8242
|
_Core.log("Storage service initialized", verbose);
|
|
8056
8243
|
},
|
|
8057
8244
|
verbose
|
|
@@ -8075,8 +8262,9 @@ var Core = class _Core {
|
|
|
8075
8262
|
await _Core.initService(
|
|
8076
8263
|
"notifications",
|
|
8077
8264
|
async () => {
|
|
8078
|
-
await
|
|
8079
|
-
|
|
8265
|
+
const { NotificationService: NotificationService2 } = await Promise.resolve().then(() => (init_NotificationService(), NotificationService_exports));
|
|
8266
|
+
await NotificationService2.initialize(notificationsConfig);
|
|
8267
|
+
_Core._coreServices.notifications = NotificationService2.getInstance();
|
|
8080
8268
|
_Core.log("Notifications service initialized", verbose);
|
|
8081
8269
|
},
|
|
8082
8270
|
verbose
|
|
@@ -8219,7 +8407,7 @@ var Core = class _Core {
|
|
|
8219
8407
|
* @example Core.events.on('example:created', handler)
|
|
8220
8408
|
*/
|
|
8221
8409
|
static get events() {
|
|
8222
|
-
return CoreEventManager;
|
|
8410
|
+
return exports.CoreEventManager;
|
|
8223
8411
|
}
|
|
8224
8412
|
/**
|
|
8225
8413
|
* Get database service instance
|
|
@@ -8641,11 +8829,13 @@ var Core = class _Core {
|
|
|
8641
8829
|
_Core._observabilityConfig = {};
|
|
8642
8830
|
if (_Core._coreServices.storage) {
|
|
8643
8831
|
await _Core._coreServices.storage.close();
|
|
8644
|
-
await
|
|
8832
|
+
const { StorageService: StorageService2 } = await Promise.resolve().then(() => (init_StorageService(), StorageService_exports));
|
|
8833
|
+
await StorageService2.reset();
|
|
8645
8834
|
}
|
|
8646
8835
|
if (_Core._coreServices.notifications) {
|
|
8647
8836
|
await _Core._coreServices.notifications.close();
|
|
8648
|
-
await
|
|
8837
|
+
const { NotificationService: NotificationService2 } = await Promise.resolve().then(() => (init_NotificationService(), NotificationService_exports));
|
|
8838
|
+
await NotificationService2.reset();
|
|
8649
8839
|
}
|
|
8650
8840
|
if (_Core._errorHandler) {
|
|
8651
8841
|
_Core._errorHandler.destroy();
|
|
@@ -8667,7 +8857,7 @@ var Core = class _Core {
|
|
|
8667
8857
|
}
|
|
8668
8858
|
_Core._flagConfig = {};
|
|
8669
8859
|
ServiceRegistry.disposeAll();
|
|
8670
|
-
CoreEventManager.dispose();
|
|
8860
|
+
exports.CoreEventManager.dispose();
|
|
8671
8861
|
_Core._coreServices = {
|
|
8672
8862
|
db: null,
|
|
8673
8863
|
api: null,
|
|
@@ -8696,20 +8886,19 @@ var Core = class _Core {
|
|
|
8696
8886
|
*/
|
|
8697
8887
|
static shouldSkipDbService(skipDb, verbose) {
|
|
8698
8888
|
if (skipDb === true) {
|
|
8699
|
-
|
|
8700
|
-
console.log("[Core] Database service skipped (skipDb: true)");
|
|
8701
|
-
}
|
|
8889
|
+
_Core.log("Database service skipped (skipDb: true)", verbose);
|
|
8702
8890
|
return true;
|
|
8703
8891
|
}
|
|
8704
8892
|
const isFrontend = _Core.isFrontend;
|
|
8705
8893
|
if (isFrontend) {
|
|
8706
8894
|
if (skipDb === false) {
|
|
8707
|
-
|
|
8708
|
-
"
|
|
8895
|
+
_Core.logger.warn(
|
|
8896
|
+
"DbService cannot be initialized on frontend runtime. DbService is backend-only and requires Node.js. Use API calls from frontend instead. Skipping database initialization."
|
|
8709
8897
|
);
|
|
8710
8898
|
} else if (verbose) {
|
|
8711
|
-
|
|
8712
|
-
"
|
|
8899
|
+
_Core.log(
|
|
8900
|
+
"Database service skipped (frontend runtime detected). DbService is only available on backend.",
|
|
8901
|
+
verbose
|
|
8713
8902
|
);
|
|
8714
8903
|
}
|
|
8715
8904
|
return true;
|
|
@@ -8721,10 +8910,9 @@ var Core = class _Core {
|
|
|
8721
8910
|
* Config validation is handled by DbService
|
|
8722
8911
|
*/
|
|
8723
8912
|
static async initializeDb(config, verbose) {
|
|
8724
|
-
|
|
8725
|
-
|
|
8726
|
-
|
|
8727
|
-
return DbService.initialize({
|
|
8913
|
+
_Core.log(`Initializing database with adapter: ${config?.adapter ?? "drizzle"}`, verbose);
|
|
8914
|
+
const { DbService: DbService2 } = await Promise.resolve().then(() => (init_DbService(), DbService_exports));
|
|
8915
|
+
return DbService2.initialize({
|
|
8728
8916
|
adapter: "drizzle",
|
|
8729
8917
|
...config
|
|
8730
8918
|
});
|
|
@@ -8736,9 +8924,7 @@ var Core = class _Core {
|
|
|
8736
8924
|
static async initializeApi(config, globalEnvironment, verbose) {
|
|
8737
8925
|
const { env: apiEnv, setAsDefault, ...apiClientOptions } = config ?? {};
|
|
8738
8926
|
const resolvedEnv = apiEnv ?? globalEnvironment ?? "development";
|
|
8739
|
-
|
|
8740
|
-
console.log(`[Core] Initializing API client for environment: ${resolvedEnv}`);
|
|
8741
|
-
}
|
|
8927
|
+
_Core.log(`Initializing API client for environment: ${resolvedEnv}`, verbose);
|
|
8742
8928
|
await ApiClientService.init(
|
|
8743
8929
|
{
|
|
8744
8930
|
env: resolvedEnv,
|
|
@@ -8752,9 +8938,7 @@ var Core = class _Core {
|
|
|
8752
8938
|
* Config validation is handled by CacheService
|
|
8753
8939
|
*/
|
|
8754
8940
|
static async initializeCache(config, verbose) {
|
|
8755
|
-
|
|
8756
|
-
console.log(`[Core] Initializing cache with strategy: ${config.strategy}`);
|
|
8757
|
-
}
|
|
8941
|
+
_Core.log(`Initializing cache with strategy: ${config.strategy}`, verbose);
|
|
8758
8942
|
await CacheService.initialize(config);
|
|
8759
8943
|
}
|
|
8760
8944
|
/**
|
|
@@ -8762,9 +8946,7 @@ var Core = class _Core {
|
|
|
8762
8946
|
* Always includes LoggerAdapter as failover for console output.
|
|
8763
8947
|
*/
|
|
8764
8948
|
static async initializeObservability(config, verbose, environment) {
|
|
8765
|
-
|
|
8766
|
-
console.log(`[Core] Initializing observability with provider: ${config.provider ?? "auto"}`);
|
|
8767
|
-
}
|
|
8949
|
+
_Core.log(`Initializing observability with provider: ${config.provider ?? "auto"}`, verbose);
|
|
8768
8950
|
const env = config.environment ?? environment ?? "development";
|
|
8769
8951
|
_Core._observabilityConfig = { ...config, environment: env };
|
|
8770
8952
|
const adapters = [];
|
|
@@ -8788,11 +8970,10 @@ var Core = class _Core {
|
|
|
8788
8970
|
flushInterval: config.flushInterval,
|
|
8789
8971
|
adapterTimeout: config.flushInterval
|
|
8790
8972
|
});
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
}
|
|
8973
|
+
_Core.log(
|
|
8974
|
+
`ObservabilityService initialized with ${adapters.length} adapters (env: ${env})`,
|
|
8975
|
+
verbose
|
|
8976
|
+
);
|
|
8796
8977
|
_Core._coreServices.observability = observability;
|
|
8797
8978
|
ServiceRegistry.setObservabilityInstance(observability);
|
|
8798
8979
|
}
|
|
@@ -8803,10 +8984,10 @@ var Core = class _Core {
|
|
|
8803
8984
|
static initializeRootStore(config, verbose) {
|
|
8804
8985
|
const isFrontend = typeof window !== "undefined";
|
|
8805
8986
|
if (isFrontend) {
|
|
8806
|
-
|
|
8987
|
+
_Core.log("Using frontend root store (Zustand)", verbose);
|
|
8807
8988
|
_Core._rootStore = store.useRootStore;
|
|
8808
8989
|
} else {
|
|
8809
|
-
|
|
8990
|
+
_Core.log("Creating backend composite store (in-memory)", verbose);
|
|
8810
8991
|
const backendErrorStore = middleware.ServerErrorMiddleware.createErrorStore({
|
|
8811
8992
|
maxErrors: config?.maxErrors ?? DEFAULT_MAX_ERRORS,
|
|
8812
8993
|
onErrorAdded: config?.onError
|
|
@@ -8854,11 +9035,11 @@ var Core = class _Core {
|
|
|
8854
9035
|
// eslint-disable-next-line complexity
|
|
8855
9036
|
static async initializeErrorHandler(config, verbose) {
|
|
8856
9037
|
if (config?.enabled === false) {
|
|
8857
|
-
|
|
9038
|
+
_Core.log("Error handler disabled by configuration", verbose);
|
|
8858
9039
|
return;
|
|
8859
9040
|
}
|
|
8860
9041
|
_Core._errorConfig = config ?? {};
|
|
8861
|
-
|
|
9042
|
+
_Core.log("Initializing global error handler...", verbose);
|
|
8862
9043
|
errors.initializeErrorSystem({
|
|
8863
9044
|
defaultLocale: _Core._errorConfig.localization?.defaultLocale,
|
|
8864
9045
|
fallbackLocale: _Core._errorConfig.localization?.fallbackLocale,
|
|
@@ -8866,13 +9047,13 @@ var Core = class _Core {
|
|
|
8866
9047
|
replaceBuiltIn: _Core._errorConfig.localization?.replaceBuiltIn
|
|
8867
9048
|
});
|
|
8868
9049
|
_Core.initializeRootStore(_Core._errorConfig, verbose);
|
|
8869
|
-
middleware.setEventEmitter(CoreEventManager.emit.bind(CoreEventManager));
|
|
9050
|
+
middleware.setEventEmitter(exports.CoreEventManager.emit.bind(exports.CoreEventManager));
|
|
8870
9051
|
const errorStore = _Core.getErrorStoreActions();
|
|
8871
9052
|
_Core._errorHandler = middleware.initializeGlobalErrorHandler(
|
|
8872
9053
|
errorStore,
|
|
8873
9054
|
_Core.buildErrorHandlerConfig(_Core._errorConfig)
|
|
8874
9055
|
);
|
|
8875
|
-
const errorEventCleanup = CoreEventManager.on(
|
|
9056
|
+
const errorEventCleanup = exports.CoreEventManager.on(
|
|
8876
9057
|
core.CORE_EVENTS.SYSTEM.ERROR,
|
|
8877
9058
|
(event) => {
|
|
8878
9059
|
if (!_Core._rootStore) return;
|
|
@@ -8880,16 +9061,15 @@ var Core = class _Core {
|
|
|
8880
9061
|
const { errors } = event.data;
|
|
8881
9062
|
if (errors && errors.length > 0) {
|
|
8882
9063
|
_Core._rootStore.getState().errors.addErrors(errors);
|
|
8883
|
-
|
|
9064
|
+
_Core.log(`Added ${errors.length} error(s) to store`, verbose);
|
|
8884
9065
|
}
|
|
8885
9066
|
} catch (e) {
|
|
8886
|
-
|
|
9067
|
+
_Core.logger.error("Failed to handle error event", { error: e });
|
|
8887
9068
|
}
|
|
8888
9069
|
}
|
|
8889
9070
|
);
|
|
8890
9071
|
_Core._eventCleanupFns.push(errorEventCleanup);
|
|
8891
|
-
|
|
8892
|
-
console.log("[Core] Global error handler initialized with CoreEventManager integration");
|
|
9072
|
+
_Core.log("Global error handler initialized with CoreEventManager integration", verbose);
|
|
8893
9073
|
if (_Core._errorConfig.httpHandler !== false) {
|
|
8894
9074
|
_Core.createHttpErrorHandler(_Core._errorConfig, verbose);
|
|
8895
9075
|
}
|
|
@@ -8911,11 +9091,11 @@ var Core = class _Core {
|
|
|
8911
9091
|
switch (runtime) {
|
|
8912
9092
|
case "express":
|
|
8913
9093
|
_Core._httpErrorHandler = middleware.ServerErrorMiddleware.createHttpErrorHandler(httpConfig);
|
|
8914
|
-
|
|
9094
|
+
_Core.log("Created Express error handler middleware", verbose);
|
|
8915
9095
|
break;
|
|
8916
9096
|
case "nestjs":
|
|
8917
9097
|
_Core._httpErrorHandler = middleware.ServerErrorMiddleware.createNestJsExceptionFilter(httpConfig);
|
|
8918
|
-
|
|
9098
|
+
_Core.log("Created NestJS exception filter", verbose);
|
|
8919
9099
|
break;
|
|
8920
9100
|
case "nextjs":
|
|
8921
9101
|
_Core._httpErrorHandler = {
|
|
@@ -8927,7 +9107,7 @@ var Core = class _Core {
|
|
|
8927
9107
|
/** For Pages Router: export default createNextApiErrorHandler()(handler) */
|
|
8928
9108
|
createNextApiErrorHandler: /* @__PURE__ */ __name((overrideConfig = {}) => middleware.ServerErrorMiddleware.createNextApiErrorHandler({ ...httpConfig, ...overrideConfig }), "createNextApiErrorHandler")
|
|
8929
9109
|
};
|
|
8930
|
-
|
|
9110
|
+
_Core.log("Created Next.js error handlers", verbose);
|
|
8931
9111
|
break;
|
|
8932
9112
|
case "node":
|
|
8933
9113
|
case "bun":
|
|
@@ -8944,15 +9124,15 @@ var Core = class _Core {
|
|
|
8944
9124
|
/** Create middleware for use with native http */
|
|
8945
9125
|
createMiddleware: /* @__PURE__ */ __name(() => middleware.ServerErrorMiddleware.createHttpErrorHandler(httpConfig), "createMiddleware")
|
|
8946
9126
|
};
|
|
8947
|
-
|
|
9127
|
+
_Core.log(`Created ${runtime} error handler`, verbose);
|
|
8948
9128
|
break;
|
|
8949
9129
|
case "browser":
|
|
8950
9130
|
_Core._httpErrorHandler = null;
|
|
8951
|
-
|
|
9131
|
+
_Core.log("Skipping HTTP handler for browser runtime", verbose);
|
|
8952
9132
|
break;
|
|
8953
9133
|
default:
|
|
8954
9134
|
_Core._httpErrorHandler = middleware.ServerErrorMiddleware.createHttpErrorHandler(httpConfig);
|
|
8955
|
-
|
|
9135
|
+
_Core.log(`Created generic HTTP error handler for ${runtime}`, verbose);
|
|
8956
9136
|
}
|
|
8957
9137
|
}
|
|
8958
9138
|
/**
|
|
@@ -8966,37 +9146,35 @@ var Core = class _Core {
|
|
|
8966
9146
|
if (!_Core._errorHandler) {
|
|
8967
9147
|
return;
|
|
8968
9148
|
}
|
|
8969
|
-
|
|
8970
|
-
|
|
8971
|
-
}
|
|
8972
|
-
const cleanupSystemError = CoreEventManager.on(core.CORE_EVENTS.SYSTEM.ERROR, (event) => {
|
|
9149
|
+
_Core.log("Subscribing to error events...", verbose);
|
|
9150
|
+
const cleanupSystemError = exports.CoreEventManager.on(core.CORE_EVENTS.SYSTEM.ERROR, (event) => {
|
|
8973
9151
|
if (_Core._errorHandler && event.data?.errors?.length) {
|
|
8974
9152
|
for (const err of event.data.errors) {
|
|
8975
9153
|
_Core._errorHandler.captureError(err, "system");
|
|
8976
9154
|
}
|
|
8977
9155
|
}
|
|
8978
9156
|
});
|
|
8979
|
-
const cleanupEntityError = CoreEventManager.on(core.CORE_EVENTS.ENTITY.ERROR, (event) => {
|
|
9157
|
+
const cleanupEntityError = exports.CoreEventManager.on(core.CORE_EVENTS.ENTITY.ERROR, (event) => {
|
|
8980
9158
|
if (_Core._errorHandler && event.data) {
|
|
8981
9159
|
_Core._errorHandler.captureError(event.data.error, "entity");
|
|
8982
9160
|
}
|
|
8983
9161
|
});
|
|
8984
|
-
const cleanupApiError = CoreEventManager.on(core.CORE_EVENTS.API.REQUEST_ERROR, (event) => {
|
|
9162
|
+
const cleanupApiError = exports.CoreEventManager.on(core.CORE_EVENTS.API.REQUEST_ERROR, (event) => {
|
|
8985
9163
|
if (_Core._errorHandler && event.data) {
|
|
8986
9164
|
_Core._errorHandler.captureError(event.data.error, "api");
|
|
8987
9165
|
}
|
|
8988
9166
|
});
|
|
8989
|
-
const cleanupValidationError = CoreEventManager.on(core.CORE_EVENTS.VALIDATION.FAILED, (event) => {
|
|
9167
|
+
const cleanupValidationError = exports.CoreEventManager.on(core.CORE_EVENTS.VALIDATION.FAILED, (event) => {
|
|
8990
9168
|
if (_Core._errorHandler && event.data) {
|
|
8991
9169
|
_Core._errorHandler.captureError(event.data.error, "validation");
|
|
8992
9170
|
}
|
|
8993
9171
|
});
|
|
8994
|
-
const cleanupAuthUnauthorized = CoreEventManager.on(core.CORE_EVENTS.AUTH.UNAUTHORIZED, (event) => {
|
|
9172
|
+
const cleanupAuthUnauthorized = exports.CoreEventManager.on(core.CORE_EVENTS.AUTH.UNAUTHORIZED, (event) => {
|
|
8995
9173
|
if (_Core._errorHandler && event.data?.error) {
|
|
8996
9174
|
_Core._errorHandler.captureError(event.data.error, "auth");
|
|
8997
9175
|
}
|
|
8998
9176
|
});
|
|
8999
|
-
const cleanupAuthSessionExpired = CoreEventManager.on(
|
|
9177
|
+
const cleanupAuthSessionExpired = exports.CoreEventManager.on(
|
|
9000
9178
|
core.CORE_EVENTS.AUTH.SESSION_EXPIRED,
|
|
9001
9179
|
(event) => {
|
|
9002
9180
|
if (_Core._errorHandler && event.data?.error) {
|
|
@@ -9013,19 +9191,19 @@ var Core = class _Core {
|
|
|
9013
9191
|
cleanupAuthSessionExpired
|
|
9014
9192
|
);
|
|
9015
9193
|
if (_Core.isRuntimeCompatible("backend")) {
|
|
9016
|
-
const cleanupDatabaseError = CoreEventManager.on(core.CORE_EVENTS.DATABASE.ERROR, (event) => {
|
|
9194
|
+
const cleanupDatabaseError = exports.CoreEventManager.on(core.CORE_EVENTS.DATABASE.ERROR, (event) => {
|
|
9017
9195
|
if (_Core._errorHandler && event.data) {
|
|
9018
9196
|
_Core._errorHandler.captureError(event.data.error, "database");
|
|
9019
9197
|
}
|
|
9020
9198
|
});
|
|
9021
9199
|
_Core._eventCleanupFns.push(cleanupDatabaseError);
|
|
9022
|
-
const cleanupStorageError = CoreEventManager.on(core.CORE_EVENTS.STORAGE.ERROR, (event) => {
|
|
9200
|
+
const cleanupStorageError = exports.CoreEventManager.on(core.CORE_EVENTS.STORAGE.ERROR, (event) => {
|
|
9023
9201
|
if (_Core._errorHandler && event.data) {
|
|
9024
9202
|
_Core._errorHandler.captureError(event.data.error, "storage");
|
|
9025
9203
|
}
|
|
9026
9204
|
});
|
|
9027
9205
|
_Core._eventCleanupFns.push(cleanupStorageError);
|
|
9028
|
-
const cleanupNotificationError = CoreEventManager.on(
|
|
9206
|
+
const cleanupNotificationError = exports.CoreEventManager.on(
|
|
9029
9207
|
core.CORE_EVENTS.NOTIFICATION.ERROR,
|
|
9030
9208
|
(event) => {
|
|
9031
9209
|
if (_Core._errorHandler && event.data) {
|
|
@@ -9034,21 +9212,17 @@ var Core = class _Core {
|
|
|
9034
9212
|
}
|
|
9035
9213
|
);
|
|
9036
9214
|
_Core._eventCleanupFns.push(cleanupNotificationError);
|
|
9037
|
-
|
|
9038
|
-
console.log("[Core] Subscribed to backend error events (database, storage, notification)");
|
|
9039
|
-
}
|
|
9215
|
+
_Core.log("Subscribed to backend error events (database, storage, notification)", verbose);
|
|
9040
9216
|
}
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
eventTypes.push("database", "storage", "notification");
|
|
9045
|
-
}
|
|
9046
|
-
console.log(`[Core] Subscribed to error events: ${eventTypes.join(", ")}`);
|
|
9217
|
+
const eventTypes = ["system", "entity", "api", "validation", "auth"];
|
|
9218
|
+
if (_Core.isRuntimeCompatible("backend")) {
|
|
9219
|
+
eventTypes.push("database", "storage", "notification");
|
|
9047
9220
|
}
|
|
9221
|
+
_Core.log(`Subscribed to error events: ${eventTypes.join(", ")}`, verbose);
|
|
9048
9222
|
}
|
|
9049
9223
|
/** Handle fetch flags error and return empty flags */
|
|
9050
9224
|
static handleFetchFlagsError(error, config, verbose) {
|
|
9051
|
-
if (verbose)
|
|
9225
|
+
if (verbose) _Core.logger.error("Failed to fetch feature flags", { error });
|
|
9052
9226
|
const packageError = error instanceof errors.BaseError ? error : new errors.CorePackageError(
|
|
9053
9227
|
error instanceof Error ? error.message : String(error),
|
|
9054
9228
|
types.ERROR_CODES.CORE_FEATURE_FLAG_PROVIDER_ERROR,
|
|
@@ -9081,7 +9255,7 @@ var Core = class _Core {
|
|
|
9081
9255
|
*/
|
|
9082
9256
|
static async initializeFeatureFlags(config, verbose) {
|
|
9083
9257
|
if (config?.enabled === false) {
|
|
9084
|
-
|
|
9258
|
+
_Core.log("Feature flags disabled by configuration", verbose);
|
|
9085
9259
|
return;
|
|
9086
9260
|
}
|
|
9087
9261
|
if (!_Core._rootStore) {
|
|
@@ -9091,7 +9265,7 @@ var Core = class _Core {
|
|
|
9091
9265
|
);
|
|
9092
9266
|
}
|
|
9093
9267
|
_Core._flagConfig = config ?? {};
|
|
9094
|
-
|
|
9268
|
+
_Core.log("Initializing feature flags from root store...", verbose);
|
|
9095
9269
|
const state = _Core._rootStore.getState();
|
|
9096
9270
|
await state.featureFlags.initialize({
|
|
9097
9271
|
defaults: _Core._flagConfig.defaults,
|
|
@@ -9100,9 +9274,12 @@ var Core = class _Core {
|
|
|
9100
9274
|
onFlagChange: _Core._flagConfig.onFlagChange,
|
|
9101
9275
|
onError: _Core._flagConfig.onError
|
|
9102
9276
|
});
|
|
9103
|
-
|
|
9277
|
+
_Core.log("Feature flags initialized", verbose);
|
|
9104
9278
|
}
|
|
9105
9279
|
};
|
|
9280
|
+
|
|
9281
|
+
// src/entry-frontend.ts
|
|
9282
|
+
init_CoreEventManager();
|
|
9106
9283
|
var errorContainerStyle = {
|
|
9107
9284
|
padding: "20px",
|
|
9108
9285
|
color: "red",
|
|
@@ -9146,6 +9323,9 @@ function InitializationLoading({ loading }) {
|
|
|
9146
9323
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: loadingContainerStyle, children: /* @__PURE__ */ jsxRuntime.jsx("p", { style: loadingMessageStyle, children: "Initializing..." }) });
|
|
9147
9324
|
}
|
|
9148
9325
|
__name(InitializationLoading, "InitializationLoading");
|
|
9326
|
+
|
|
9327
|
+
// src/engine/featureFlags/engine.ts
|
|
9328
|
+
init_common();
|
|
9149
9329
|
var FeatureFlagContextBuilder = class _FeatureFlagContextBuilder {
|
|
9150
9330
|
constructor() {
|
|
9151
9331
|
this.context = {};
|
|
@@ -10264,11 +10444,19 @@ var ApiFeatureFlagProvider = class extends FeatureFlagProvider {
|
|
|
10264
10444
|
return this.engine.getRules();
|
|
10265
10445
|
}
|
|
10266
10446
|
};
|
|
10447
|
+
init_DbService();
|
|
10448
|
+
|
|
10449
|
+
// src/models/featureFlags/FeatureFlagRepository.ts
|
|
10450
|
+
init_common();
|
|
10451
|
+
init_DbService();
|
|
10267
10452
|
({
|
|
10268
10453
|
flagsTable: core.FEATURE_FLAG_TABLE.FeatureFlags,
|
|
10269
10454
|
rulesTable: core.FEATURE_FLAG_TABLE.FeatureFlagRules,
|
|
10270
10455
|
evaluationsTable: core.FEATURE_FLAG_TABLE.FeatureFlagEvaluations,
|
|
10271
10456
|
overridesTable: core.FEATURE_FLAG_TABLE.FeatureFlagOverrides});
|
|
10457
|
+
|
|
10458
|
+
// src/domain/featureFlags/FrontendFeatureFlagDomainService.ts
|
|
10459
|
+
init_CoreEventManager();
|
|
10272
10460
|
var DEFAULT_CACHE_TTL_SECONDS2 = 300;
|
|
10273
10461
|
var DEFAULT_REFRESH_INTERVAL_MS = 6e4;
|
|
10274
10462
|
var FrontendFeatureFlagDomainService = class _FrontendFeatureFlagDomainService extends BaseFrontendDomainService {
|
|
@@ -10289,7 +10477,7 @@ var FrontendFeatureFlagDomainService = class _FrontendFeatureFlagDomainService e
|
|
|
10289
10477
|
this.provider = config.provider;
|
|
10290
10478
|
this._initialized = true;
|
|
10291
10479
|
this.logInfo("Service initialized", { storeCount: this.connectedStoreCount });
|
|
10292
|
-
CoreEventManager.emit("featureFlag:provider:initialized", { providerType: "api" });
|
|
10480
|
+
exports.CoreEventManager.emit("featureFlag:provider:initialized", { providerType: "api" });
|
|
10293
10481
|
}
|
|
10294
10482
|
static {
|
|
10295
10483
|
__name(this, "FrontendFeatureFlagDomainService");
|
|
@@ -10344,7 +10532,7 @@ var FrontendFeatureFlagDomainService = class _FrontendFeatureFlagDomainService e
|
|
|
10344
10532
|
}
|
|
10345
10533
|
dispose() {
|
|
10346
10534
|
this.provider?.dispose();
|
|
10347
|
-
CoreEventManager.emit("featureFlag:provider:disposed", {});
|
|
10535
|
+
exports.CoreEventManager.emit("featureFlag:provider:disposed", {});
|
|
10348
10536
|
this.logInfo("Service disposed");
|
|
10349
10537
|
}
|
|
10350
10538
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -10355,7 +10543,7 @@ var FrontendFeatureFlagDomainService = class _FrontendFeatureFlagDomainService e
|
|
|
10355
10543
|
this.setStoresLoading(true);
|
|
10356
10544
|
try {
|
|
10357
10545
|
await this.provider.refresh();
|
|
10358
|
-
CoreEventManager.emit("featureFlag:provider:refreshed", {});
|
|
10546
|
+
exports.CoreEventManager.emit("featureFlag:provider:refreshed", {});
|
|
10359
10547
|
if (this.hasConnectedStores) {
|
|
10360
10548
|
const evaluations = await this.provider.getAllFlags(this.config.defaultContext);
|
|
10361
10549
|
this.syncToStores(this.toValues(evaluations));
|
|
@@ -10371,16 +10559,16 @@ var FrontendFeatureFlagDomainService = class _FrontendFeatureFlagDomainService e
|
|
|
10371
10559
|
async evaluate(key, context) {
|
|
10372
10560
|
this.assertReady();
|
|
10373
10561
|
const ctx = context ?? this.config.defaultContext;
|
|
10374
|
-
CoreEventManager.emit("featureFlag:evaluating", { key, context: ctx });
|
|
10562
|
+
exports.CoreEventManager.emit("featureFlag:evaluating", { key, context: ctx });
|
|
10375
10563
|
try {
|
|
10376
10564
|
const result = await this.provider.getFlag(key, ctx);
|
|
10377
|
-
CoreEventManager.emit("featureFlag:evaluated", { key, result });
|
|
10565
|
+
exports.CoreEventManager.emit("featureFlag:evaluated", { key, result });
|
|
10378
10566
|
if (this.hasConnectedStores) {
|
|
10379
10567
|
this.syncToStores({ [key]: result.value });
|
|
10380
10568
|
}
|
|
10381
10569
|
return result;
|
|
10382
10570
|
} catch (error) {
|
|
10383
|
-
CoreEventManager.emit("featureFlag:evaluate:error", { key, error });
|
|
10571
|
+
exports.CoreEventManager.emit("featureFlag:evaluate:error", { key, error });
|
|
10384
10572
|
throw error;
|
|
10385
10573
|
}
|
|
10386
10574
|
}
|
|
@@ -10417,49 +10605,49 @@ var FrontendFeatureFlagDomainService = class _FrontendFeatureFlagDomainService e
|
|
|
10417
10605
|
// ─────────────────────────────────────────────────────────────────────────
|
|
10418
10606
|
async createFlag(data) {
|
|
10419
10607
|
this.assertReady();
|
|
10420
|
-
CoreEventManager.emit("featureFlag:creating", { data });
|
|
10608
|
+
exports.CoreEventManager.emit("featureFlag:creating", { data });
|
|
10421
10609
|
try {
|
|
10422
10610
|
const flag = await this.provider.createFlag(data);
|
|
10423
|
-
CoreEventManager.emit("featureFlag:created", { flag });
|
|
10611
|
+
exports.CoreEventManager.emit("featureFlag:created", { flag });
|
|
10424
10612
|
this.logInfo("Flag created", { key: data.key });
|
|
10425
10613
|
if (this.hasConnectedStores) {
|
|
10426
10614
|
this.syncToStores({ [data.key]: flag.value });
|
|
10427
10615
|
}
|
|
10428
10616
|
return flag;
|
|
10429
10617
|
} catch (error) {
|
|
10430
|
-
CoreEventManager.emit("featureFlag:create:error", { error, data });
|
|
10618
|
+
exports.CoreEventManager.emit("featureFlag:create:error", { error, data });
|
|
10431
10619
|
throw error;
|
|
10432
10620
|
}
|
|
10433
10621
|
}
|
|
10434
10622
|
async updateFlag(key, data) {
|
|
10435
10623
|
this.assertReady();
|
|
10436
|
-
CoreEventManager.emit("featureFlag:updating", { key, data });
|
|
10624
|
+
exports.CoreEventManager.emit("featureFlag:updating", { key, data });
|
|
10437
10625
|
try {
|
|
10438
10626
|
const flag = await this.provider.updateFlag(key, data);
|
|
10439
|
-
CoreEventManager.emit("featureFlag:updated", { flag });
|
|
10627
|
+
exports.CoreEventManager.emit("featureFlag:updated", { flag });
|
|
10440
10628
|
this.logInfo("Flag updated", { key });
|
|
10441
10629
|
if (this.hasConnectedStores) {
|
|
10442
10630
|
this.syncToStores({ [key]: flag.value });
|
|
10443
10631
|
}
|
|
10444
10632
|
return flag;
|
|
10445
10633
|
} catch (error) {
|
|
10446
|
-
CoreEventManager.emit("featureFlag:update:error", { error, key });
|
|
10634
|
+
exports.CoreEventManager.emit("featureFlag:update:error", { error, key });
|
|
10447
10635
|
throw error;
|
|
10448
10636
|
}
|
|
10449
10637
|
}
|
|
10450
10638
|
async deleteFlag(key) {
|
|
10451
10639
|
this.assertReady();
|
|
10452
|
-
CoreEventManager.emit("featureFlag:deleting", { key });
|
|
10640
|
+
exports.CoreEventManager.emit("featureFlag:deleting", { key });
|
|
10453
10641
|
try {
|
|
10454
10642
|
await this.provider.deleteFlag(key);
|
|
10455
|
-
CoreEventManager.emit("featureFlag:deleted", { key });
|
|
10643
|
+
exports.CoreEventManager.emit("featureFlag:deleted", { key });
|
|
10456
10644
|
this.logInfo("Flag deleted", { key });
|
|
10457
10645
|
if (this.hasConnectedStores) {
|
|
10458
10646
|
const evaluations = await this.provider.getAllFlags(this.config.defaultContext);
|
|
10459
10647
|
this.syncToStores(this.toValues(evaluations), true);
|
|
10460
10648
|
}
|
|
10461
10649
|
} catch (error) {
|
|
10462
|
-
CoreEventManager.emit("featureFlag:delete:error", { error, key });
|
|
10650
|
+
exports.CoreEventManager.emit("featureFlag:delete:error", { error, key });
|
|
10463
10651
|
throw error;
|
|
10464
10652
|
}
|
|
10465
10653
|
}
|
|
@@ -10479,15 +10667,15 @@ var FrontendFeatureFlagDomainService = class _FrontendFeatureFlagDomainService e
|
|
|
10479
10667
|
// ─────────────────────────────────────────────────────────────────────────
|
|
10480
10668
|
setOverride(key, value) {
|
|
10481
10669
|
this.assertReady();
|
|
10482
|
-
CoreEventManager.emit("featureFlag:override:setting", { key, value });
|
|
10670
|
+
exports.CoreEventManager.emit("featureFlag:override:setting", { key, value });
|
|
10483
10671
|
this.provider.setOverride(key, value);
|
|
10484
|
-
CoreEventManager.emit("featureFlag:override:set", { key, value });
|
|
10672
|
+
exports.CoreEventManager.emit("featureFlag:override:set", { key, value });
|
|
10485
10673
|
}
|
|
10486
10674
|
removeOverride(key) {
|
|
10487
10675
|
this.assertReady();
|
|
10488
|
-
CoreEventManager.emit("featureFlag:override:removing", { key });
|
|
10676
|
+
exports.CoreEventManager.emit("featureFlag:override:removing", { key });
|
|
10489
10677
|
this.provider.removeOverride(key);
|
|
10490
|
-
CoreEventManager.emit("featureFlag:override:removed", { key });
|
|
10678
|
+
exports.CoreEventManager.emit("featureFlag:override:removed", { key });
|
|
10491
10679
|
}
|
|
10492
10680
|
clearOverrides() {
|
|
10493
10681
|
this.assertReady();
|
|
@@ -10501,7 +10689,7 @@ var FrontendFeatureFlagDomainService = class _FrontendFeatureFlagDomainService e
|
|
|
10501
10689
|
return this.provider.subscribe(callback);
|
|
10502
10690
|
}
|
|
10503
10691
|
on(event, handler) {
|
|
10504
|
-
return CoreEventManager.on(event, handler);
|
|
10692
|
+
return exports.CoreEventManager.on(event, handler);
|
|
10505
10693
|
}
|
|
10506
10694
|
// ─────────────────────────────────────────────────────────────────────────
|
|
10507
10695
|
// Protected: Store Sync Override
|
|
@@ -10747,6 +10935,7 @@ function ApiProvider({
|
|
|
10747
10935
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
|
|
10748
10936
|
}
|
|
10749
10937
|
__name(ApiProvider, "ApiProvider");
|
|
10938
|
+
init_CoreEventManager();
|
|
10750
10939
|
var PlyazContext = react.createContext(null);
|
|
10751
10940
|
var FeatureFlagStore = class {
|
|
10752
10941
|
constructor(config = {}) {
|
|
@@ -10858,7 +11047,7 @@ __name(initializeFeatureFlags, "initializeFeatureFlags");
|
|
|
10858
11047
|
function createServicesObject(config, featureFlagStore) {
|
|
10859
11048
|
return {
|
|
10860
11049
|
api: ApiClientService,
|
|
10861
|
-
events: CoreEventManager,
|
|
11050
|
+
events: exports.CoreEventManager,
|
|
10862
11051
|
core: Core,
|
|
10863
11052
|
featureFlags: {
|
|
10864
11053
|
get: /* @__PURE__ */ __name((key, def) => featureFlagStore.get(key, def), "get"),
|
|
@@ -10917,7 +11106,7 @@ function PlyazProvider({
|
|
|
10917
11106
|
const contextValue = react.useMemo(
|
|
10918
11107
|
() => ({
|
|
10919
11108
|
api: isReady ? ApiClientService : null,
|
|
10920
|
-
events: CoreEventManager,
|
|
11109
|
+
events: exports.CoreEventManager,
|
|
10921
11110
|
core: Core,
|
|
10922
11111
|
featureFlags: {
|
|
10923
11112
|
get: /* @__PURE__ */ __name((key, def) => featureFlagStore.get(key, def), "get"),
|
|
@@ -11114,7 +11303,6 @@ exports.BaseFrontendDomainService = BaseFrontendDomainService;
|
|
|
11114
11303
|
exports.BaseMapper = BaseMapper;
|
|
11115
11304
|
exports.BaseValidator = BaseValidator;
|
|
11116
11305
|
exports.Core = Core;
|
|
11117
|
-
exports.CoreEventManager = CoreEventManager;
|
|
11118
11306
|
exports.FrontendExampleDomainService = FrontendExampleDomainService;
|
|
11119
11307
|
exports.FrontendFeatureFlagDomainService = FrontendFeatureFlagDomainService;
|
|
11120
11308
|
exports.InitializationError = InitializationError;
|