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