@futdevpro/nts-dynamo 1.15.153 → 1.15.154
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 +18 -3
- package/__documentations/2026-08-22-content-free-process-error-boundary.md +91 -0
- package/build/_models/control-models/app-params.control-model.d.ts.map +1 -1
- package/build/_models/control-models/app-params.control-model.js +1 -2
- package/build/_models/control-models/app-params.control-model.js.map +1 -1
- package/build/_modules/server/index.d.ts +4 -0
- package/build/_modules/server/index.d.ts.map +1 -1
- package/build/_modules/server/index.js +5 -0
- package/build/_modules/server/index.js.map +1 -1
- package/build/_modules/server/safe-diagnostic/safe-diagnostic-cause.type-enum.d.ts +9 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic-cause.type-enum.d.ts.map +1 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic-cause.type-enum.js +13 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic-cause.type-enum.js.map +1 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum.d.ts +20 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum.d.ts.map +1 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum.js +24 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum.js.map +1 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic.control-service.d.ts +19 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic.control-service.d.ts.map +1 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic.control-service.js +83 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic.control-service.js.map +1 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic.interface.d.ts +19 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic.interface.d.ts.map +1 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic.interface.js +3 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic.interface.js.map +1 -0
- package/build/_services/core/global.service.d.ts +3 -3
- package/build/_services/core/global.service.d.ts.map +1 -1
- package/build/_services/core/global.service.js +24 -18
- package/build/_services/core/global.service.js.map +1 -1
- package/build/_services/server/app.server.d.ts +2 -0
- package/build/_services/server/app.server.d.ts.map +1 -1
- package/build/_services/server/app.server.js +71 -199
- package/build/_services/server/app.server.js.map +1 -1
- package/package.json +1 -1
- package/src/_models/control-models/app-params.control-model.spec.ts +23 -19
- package/src/_models/control-models/app-params.control-model.ts +10 -9
- package/src/_modules/server/index.ts +8 -2
- package/src/_modules/server/safe-diagnostic/safe-diagnostic-cause.type-enum.ts +8 -0
- package/src/_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum.ts +19 -0
- package/src/_modules/server/safe-diagnostic/safe-diagnostic.control-service.spec.ts +124 -0
- package/src/_modules/server/safe-diagnostic/safe-diagnostic.control-service.ts +99 -0
- package/src/_modules/server/safe-diagnostic/safe-diagnostic.interface.ts +20 -0
- package/src/_modules/server/server-status/db-readiness-probe.control-service.spec.ts +2 -2
- package/src/_services/core/global.service.ts +55 -54
- package/src/_services/server/app-safe-diagnostic.spec.ts +124 -0
- package/src/_services/server/app.server.ts +294 -347
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
1
|
+
import {
|
|
5
2
|
DyFM_AnyError,
|
|
6
3
|
DyFM_Array,
|
|
7
4
|
DyFM_DataModel_Params,
|
|
@@ -9,25 +6,28 @@ import {
|
|
|
9
6
|
DyFM_Error_Settings,
|
|
10
7
|
DyFM_global_settings,
|
|
11
8
|
DyFM_Log,
|
|
12
|
-
DyFM_Metadata
|
|
13
|
-
} from '@futdevpro/fsm-dynamo';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} from '
|
|
20
|
-
|
|
21
|
-
import {
|
|
22
|
-
/* import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
import {
|
|
9
|
+
DyFM_Metadata
|
|
10
|
+
} from '@futdevpro/fsm-dynamo';
|
|
11
|
+
|
|
12
|
+
import { DyNTS_AuthService } from './auth.service';
|
|
13
|
+
import { DyNTS_EmailService } from './email.service';
|
|
14
|
+
import { DyNTS_Service_Collection } from './service-collection.service';
|
|
15
|
+
/* import { DyNTS_DBServiceCollection } from './db-service-collection.service'; */
|
|
16
|
+
/* import { DyNTS_EmailServiceCollection } from './email-service-collection.service'; */
|
|
17
|
+
import { DyNTS_DBService } from '../base/db.service';
|
|
18
|
+
import { DyNTS_SingletonService } from '../base/singleton.service';
|
|
19
|
+
/* import { DyNTS_archiveFlag } from '../../_collections/archive-flag.const'; */
|
|
20
|
+
import { DyNTS_getArchivedDBName } from '../../_collections/archive.util';
|
|
21
|
+
import { DyNTS_global_settings } from '../../_collections/global-settings.const';
|
|
22
|
+
import { DyNTS_App_Params } from '../../_models/control-models/app-params.control-model';
|
|
23
|
+
import {
|
|
24
|
+
DyNTS_GlobalErrorHandlerFn,
|
|
25
|
+
DyNTS_GlobalService_Settings
|
|
26
|
+
} from '../../_models/interfaces/global-service-settings.interface';
|
|
27
|
+
import { DyNTS_SafeDiagnosticStage_Type } from '../../_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum';
|
|
28
|
+
import {
|
|
29
|
+
DyNTS_SafeDiagnostic_ControlService
|
|
30
|
+
} from '../../_modules/server/safe-diagnostic/safe-diagnostic.control-service';
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* This is the main Global/Core Service Collection used by DynamoNTS,
|
|
@@ -74,10 +74,14 @@ export class DyNTS_GlobalService extends DyNTS_SingletonService {
|
|
|
74
74
|
`We encountered an unhandled Control Service Error, ` +
|
|
75
75
|
`\nplease contact the responsible development team.`;
|
|
76
76
|
|
|
77
|
-
static globalErrorHandler: DyNTS_GlobalErrorHandlerFn =
|
|
78
|
-
async (error: DyFM_AnyError): Promise<void> => {
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
static globalErrorHandler: DyNTS_GlobalErrorHandlerFn =
|
|
78
|
+
async (error: DyFM_AnyError): Promise<void> => {
|
|
79
|
+
DyNTS_SafeDiagnostic_ControlService.report({
|
|
80
|
+
code: 'DYNTS|GLOBAL|HANDLER_NOT_SET',
|
|
81
|
+
stage: DyNTS_SafeDiagnosticStage_Type.globalErrorHandler,
|
|
82
|
+
error: error,
|
|
83
|
+
});
|
|
84
|
+
};
|
|
81
85
|
|
|
82
86
|
/**
|
|
83
87
|
* You need to setup global Services through this function
|
|
@@ -179,16 +183,16 @@ export class DyNTS_GlobalService extends DyNTS_SingletonService {
|
|
|
179
183
|
return;
|
|
180
184
|
}
|
|
181
185
|
|
|
182
|
-
try {
|
|
183
|
-
this.instance.dbServiceCollection[dbModel.dataName] = new DyNTS_DBService(dbModel);
|
|
184
|
-
} catch (error) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
throw new DyFM_Error({
|
|
186
|
+
try {
|
|
187
|
+
this.instance.dbServiceCollection[dbModel.dataName] = new DyNTS_DBService(dbModel);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
DyNTS_SafeDiagnostic_ControlService.report({
|
|
190
|
+
code: 'DYNTS|GLOBAL|DB_SERVICE_CREATE_FAILED',
|
|
191
|
+
stage: DyNTS_SafeDiagnosticStage_Type.bootstrap,
|
|
192
|
+
error: error,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
throw new DyFM_Error({
|
|
192
196
|
...this.getDefaultErrorSettings('setDBServices', error, 'DyNTS_GlobalService'),
|
|
193
197
|
|
|
194
198
|
additionalContent: {
|
|
@@ -265,7 +269,7 @@ export class DyNTS_GlobalService extends DyNTS_SingletonService {
|
|
|
265
269
|
|
|
266
270
|
private static async setErrorHandler(errorHandler?: DyNTS_GlobalErrorHandlerFn): Promise<void> {
|
|
267
271
|
try {
|
|
268
|
-
DyNTS_GlobalService.globalErrorHandler = (async (error:
|
|
272
|
+
DyNTS_GlobalService.globalErrorHandler = (async (error: unknown): Promise<void> => {
|
|
269
273
|
try {
|
|
270
274
|
if (errorHandler) {
|
|
271
275
|
await errorHandler(error);
|
|
@@ -280,22 +284,19 @@ export class DyNTS_GlobalService extends DyNTS_SingletonService {
|
|
|
280
284
|
'please check if it is using the same node_modules as the app)'
|
|
281
285
|
);
|
|
282
286
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
error
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
});
|
|
287
|
+
DyNTS_SafeDiagnostic_ControlService.report({
|
|
288
|
+
code: 'DYNTS|GLOBAL|UNHANDLED_ERROR',
|
|
289
|
+
stage: DyNTS_SafeDiagnosticStage_Type.globalErrorHandler,
|
|
290
|
+
error: error,
|
|
291
|
+
});
|
|
292
|
+
} catch (error) {
|
|
293
|
+
DyNTS_SafeDiagnostic_ControlService.report({
|
|
294
|
+
code: 'DYNTS|GLOBAL|HANDLER_FAILED',
|
|
295
|
+
stage: DyNTS_SafeDiagnosticStage_Type.globalErrorHandler,
|
|
296
|
+
error: error,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
});
|
|
299
300
|
} catch (error) {
|
|
300
301
|
throw new DyFM_Error({
|
|
301
302
|
...this.getDefaultErrorSettings('setErrorHandler', error, 'DyNTS_GlobalService'),
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { DyFM_Log } from '@futdevpro/fsm-dynamo';
|
|
2
|
+
|
|
3
|
+
import { DyNTS_App_Params } from '../../_models/control-models/app-params.control-model';
|
|
4
|
+
import { DyNTS_Http_Settings } from '../../_models/control-models/http-settings.control-model';
|
|
5
|
+
import { DyNTS_GlobalService_Settings } from '../../_models/interfaces/global-service-settings.interface';
|
|
6
|
+
import { DyNTS_GlobalService } from '../core/global.service';
|
|
7
|
+
import { DyNTS_App } from './app.server';
|
|
8
|
+
|
|
9
|
+
class DyNTS_SafeDiagnosticTestApp extends DyNTS_App {
|
|
10
|
+
protected override asyncConstruct(): Promise<void> {
|
|
11
|
+
return Promise.resolve();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Supplies the minimal valid application parameters for the boundary test. */
|
|
15
|
+
getAppParams(): DyNTS_App_Params {
|
|
16
|
+
return new DyNTS_App_Params({
|
|
17
|
+
name: 'SafeDiagnosticTestApp',
|
|
18
|
+
version: '1.0.0',
|
|
19
|
+
dbName: 'safe_diagnostic_test',
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Keeps the test application independent from concrete global services. */
|
|
24
|
+
getGlobalServiceCollection(): DyNTS_GlobalService_Settings {
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Keeps the test application independent from a listening HTTP server. */
|
|
29
|
+
getPortSettings(): DyNTS_Http_Settings {
|
|
30
|
+
return new DyNTS_Http_Settings();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class DyNTS_FailingBootstrapTestApp extends DyNTS_SafeDiagnosticTestApp {
|
|
35
|
+
protected override asyncConstruct(): Promise<void> {
|
|
36
|
+
return Promise.reject(new Error(DyNTS_FailingBootstrapTestApp.canary));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static readonly canary: string = 'mongodb://bootstrap-user:bootstrap-secret@private-host.example/database';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe('| DyNTS_App safe process boundary', (): void => {
|
|
43
|
+
const rejectionCanary: string = 'mongodb://rejection-user:rejection-secret@private-host.example/database';
|
|
44
|
+
let originalHandler: typeof DyNTS_GlobalService.globalErrorHandler;
|
|
45
|
+
let initialListeners: NodeJS.UnhandledRejectionListener[];
|
|
46
|
+
|
|
47
|
+
beforeEach((): void => {
|
|
48
|
+
originalHandler = DyNTS_GlobalService.globalErrorHandler;
|
|
49
|
+
initialListeners = process.listeners('unhandledRejection');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
afterEach((): void => {
|
|
53
|
+
DyNTS_GlobalService.globalErrorHandler = originalHandler;
|
|
54
|
+
process.listeners('unhandledRejection').forEach((listener: NodeJS.UnhandledRejectionListener): void => {
|
|
55
|
+
if (!initialListeners.includes(listener)) {
|
|
56
|
+
process.removeListener('unhandledRejection', listener);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('| projects an unhandled rejection before logging or forwarding it', async (): Promise<void> => {
|
|
62
|
+
const logSpy: jasmine.Spy = spyOn(DyFM_Log, 'error');
|
|
63
|
+
const handlerSpy: jasmine.Spy = jasmine.createSpy('globalErrorHandler').and.resolveTo();
|
|
64
|
+
|
|
65
|
+
DyNTS_GlobalService.globalErrorHandler = handlerSpy;
|
|
66
|
+
new DyNTS_SafeDiagnosticTestApp();
|
|
67
|
+
|
|
68
|
+
const addedListeners: NodeJS.UnhandledRejectionListener[] = process
|
|
69
|
+
.listeners('unhandledRejection')
|
|
70
|
+
.filter((listener: NodeJS.UnhandledRejectionListener): boolean => !initialListeners.includes(listener));
|
|
71
|
+
|
|
72
|
+
expect(addedListeners.length).toBe(1);
|
|
73
|
+
addedListeners[0](new Error(rejectionCanary), Promise.resolve());
|
|
74
|
+
await Promise.resolve();
|
|
75
|
+
|
|
76
|
+
const serializedLogs: string = JSON.stringify(logSpy.calls.allArgs());
|
|
77
|
+
const serializedForwardedError: string = JSON.stringify(handlerSpy.calls.mostRecent().args[0]);
|
|
78
|
+
|
|
79
|
+
expect(serializedLogs).toContain('dynts-safe-diagnostic/1');
|
|
80
|
+
expect(serializedForwardedError).toContain('dynts-safe-diagnostic/1');
|
|
81
|
+
expect(serializedForwardedError).toContain('DYNTS-AS0-BASE-UR');
|
|
82
|
+
expect(handlerSpy.calls.mostRecent().args.length).toBe(1);
|
|
83
|
+
expect(serializedLogs).not.toContain(rejectionCanary);
|
|
84
|
+
expect(serializedLogs).not.toContain('rejection-secret');
|
|
85
|
+
expect(serializedForwardedError).not.toContain(rejectionCanary);
|
|
86
|
+
expect(serializedForwardedError).not.toContain('rejection-secret');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('| projects a bootstrap rejection before logging, forwarding and exiting', async (): Promise<void> => {
|
|
90
|
+
const logSpy: jasmine.Spy = spyOn(DyFM_Log, 'error');
|
|
91
|
+
const handlerSpy: jasmine.Spy = jasmine.createSpy('globalErrorHandler').and.resolveTo();
|
|
92
|
+
const exitSpy: jasmine.Spy = spyOn(process, 'exit');
|
|
93
|
+
|
|
94
|
+
DyNTS_GlobalService.globalErrorHandler = handlerSpy;
|
|
95
|
+
new DyNTS_FailingBootstrapTestApp();
|
|
96
|
+
await new Promise<void>((resolve: () => void): NodeJS.Immediate => setImmediate(resolve));
|
|
97
|
+
|
|
98
|
+
const serializedLogs: string = JSON.stringify(logSpy.calls.allArgs());
|
|
99
|
+
const serializedForwardedError: string = JSON.stringify(handlerSpy.calls.mostRecent().args[0]);
|
|
100
|
+
|
|
101
|
+
expect(exitSpy).toHaveBeenCalledOnceWith(1);
|
|
102
|
+
expect(serializedLogs).toContain('dynts-safe-diagnostic/1');
|
|
103
|
+
expect(serializedForwardedError).toContain('dynts-safe-diagnostic/1');
|
|
104
|
+
expect(serializedForwardedError).toContain('DYNTS-AS0-BOOTSTRAP-FAILED');
|
|
105
|
+
expect(handlerSpy.calls.mostRecent().args.length).toBe(1);
|
|
106
|
+
expect(serializedLogs).not.toContain(DyNTS_FailingBootstrapTestApp.canary);
|
|
107
|
+
expect(serializedLogs).not.toContain('bootstrap-secret');
|
|
108
|
+
expect(serializedForwardedError).not.toContain(DyNTS_FailingBootstrapTestApp.canary);
|
|
109
|
+
expect(serializedForwardedError).not.toContain('bootstrap-secret');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('| projects raw failures in the default global error handler', async (): Promise<void> => {
|
|
113
|
+
const logSpy: jasmine.Spy = spyOn(DyFM_Log, 'error');
|
|
114
|
+
|
|
115
|
+
await DyNTS_GlobalService.setServices({});
|
|
116
|
+
await DyNTS_GlobalService.globalErrorHandler(new Error(rejectionCanary));
|
|
117
|
+
|
|
118
|
+
const serializedLogs: string = JSON.stringify(logSpy.calls.allArgs());
|
|
119
|
+
|
|
120
|
+
expect(serializedLogs).toContain('dynts-safe-diagnostic/1');
|
|
121
|
+
expect(serializedLogs).not.toContain(rejectionCanary);
|
|
122
|
+
expect(serializedLogs).not.toContain('rejection-secret');
|
|
123
|
+
});
|
|
124
|
+
});
|