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