@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.
Files changed (46) hide show
  1. package/README.md +18 -3
  2. package/__documentations/2026-08-22-content-free-process-error-boundary.md +91 -0
  3. package/build/_models/control-models/app-params.control-model.d.ts.map +1 -1
  4. package/build/_models/control-models/app-params.control-model.js +1 -2
  5. package/build/_models/control-models/app-params.control-model.js.map +1 -1
  6. package/build/_modules/server/index.d.ts +4 -0
  7. package/build/_modules/server/index.d.ts.map +1 -1
  8. package/build/_modules/server/index.js +5 -0
  9. package/build/_modules/server/index.js.map +1 -1
  10. package/build/_modules/server/safe-diagnostic/safe-diagnostic-cause.type-enum.d.ts +9 -0
  11. package/build/_modules/server/safe-diagnostic/safe-diagnostic-cause.type-enum.d.ts.map +1 -0
  12. package/build/_modules/server/safe-diagnostic/safe-diagnostic-cause.type-enum.js +13 -0
  13. package/build/_modules/server/safe-diagnostic/safe-diagnostic-cause.type-enum.js.map +1 -0
  14. package/build/_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum.d.ts +20 -0
  15. package/build/_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum.d.ts.map +1 -0
  16. package/build/_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum.js +24 -0
  17. package/build/_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum.js.map +1 -0
  18. package/build/_modules/server/safe-diagnostic/safe-diagnostic.control-service.d.ts +19 -0
  19. package/build/_modules/server/safe-diagnostic/safe-diagnostic.control-service.d.ts.map +1 -0
  20. package/build/_modules/server/safe-diagnostic/safe-diagnostic.control-service.js +83 -0
  21. package/build/_modules/server/safe-diagnostic/safe-diagnostic.control-service.js.map +1 -0
  22. package/build/_modules/server/safe-diagnostic/safe-diagnostic.interface.d.ts +19 -0
  23. package/build/_modules/server/safe-diagnostic/safe-diagnostic.interface.d.ts.map +1 -0
  24. package/build/_modules/server/safe-diagnostic/safe-diagnostic.interface.js +3 -0
  25. package/build/_modules/server/safe-diagnostic/safe-diagnostic.interface.js.map +1 -0
  26. package/build/_services/core/global.service.d.ts +3 -3
  27. package/build/_services/core/global.service.d.ts.map +1 -1
  28. package/build/_services/core/global.service.js +24 -18
  29. package/build/_services/core/global.service.js.map +1 -1
  30. package/build/_services/server/app.server.d.ts +2 -0
  31. package/build/_services/server/app.server.d.ts.map +1 -1
  32. package/build/_services/server/app.server.js +71 -199
  33. package/build/_services/server/app.server.js.map +1 -1
  34. package/package.json +1 -1
  35. package/src/_models/control-models/app-params.control-model.spec.ts +23 -19
  36. package/src/_models/control-models/app-params.control-model.ts +10 -9
  37. package/src/_modules/server/index.ts +8 -2
  38. package/src/_modules/server/safe-diagnostic/safe-diagnostic-cause.type-enum.ts +8 -0
  39. package/src/_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum.ts +19 -0
  40. package/src/_modules/server/safe-diagnostic/safe-diagnostic.control-service.spec.ts +124 -0
  41. package/src/_modules/server/safe-diagnostic/safe-diagnostic.control-service.ts +99 -0
  42. package/src/_modules/server/safe-diagnostic/safe-diagnostic.interface.ts +20 -0
  43. package/src/_modules/server/server-status/db-readiness-probe.control-service.spec.ts +2 -2
  44. package/src/_services/core/global.service.ts +55 -54
  45. package/src/_services/server/app-safe-diagnostic.spec.ts +124 -0
  46. 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
- import { DyNTS_App_Params } from '../../_models/control-models/app-params.control-model';
15
-
16
- import {
17
- DyNTS_GlobalErrorHandlerFn,
18
- DyNTS_GlobalService_Settings
19
- } from '../../_models/interfaces/global-service-settings.interface';
20
-
21
- import { DyNTS_AuthService } from './auth.service';
22
- /* import { DyNTS_DBServiceCollection } from './db-service-collection.service'; */
23
- import { DyNTS_DBService } from '../base/db.service';
24
- import { DyNTS_EmailService } from './email.service';
25
- import { DyNTS_Service_Collection } from './service-collection.service';
26
- /* import { DyNTS_EmailServiceCollection } from './email-service-collection.service'; */
27
- import { DyNTS_global_settings } from '../../_collections/global-settings.const';
28
- import { DyNTS_SingletonService } from '../base/singleton.service';
29
- /* import { DyNTS_archiveFlag } from '../../_collections/archive-flag.const'; */
30
- import { DyNTS_getArchivedDBName } from '../../_collections/archive.util';
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
- DyFM_Log.error(`Global Error Handler not set!\nError:`, error);
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
- if (error instanceof DyFM_Error && !DyFM_global_settings.log_settings.highDetailedLogs) {
186
- error.logSimple(`Failed to create DyNTS_DBService (${dbModel.dataName}).`);
187
- } else {
188
- DyFM_Log.error(`Failed to create DyNTS_DBService (${dbModel.dataName}).`, error);
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: any): Promise<void> => {
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
- DyFM_Error.logSimple(`Error caught by globalErrorHandler.`, error);
284
- } catch (error) {
285
- if (error instanceof DyFM_Error) {
286
- error.logSimple(
287
- `NTS GLOBAL MULTILEVEL ERROR: ` +
288
- `\nFailed to handle error on globalErrorHandler.`,
289
- );
290
- } else {
291
- DyFM_Log.error(
292
- `NTS GLOBAL MULTILEVEL ERROR: ` +
293
- `\nFailed to handle error on globalErrorHandler.`,
294
- (error as DyFM_Error)?.getErrorSimplified?.() ?? error
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
+ });