@futdevpro/fsm-dynamo 1.15.13 → 1.15.15
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/build/_collections/utils/extract-error-message.util.d.ts +71 -0
- package/build/_collections/utils/extract-error-message.util.d.ts.map +1 -0
- package/build/_collections/utils/extract-error-message.util.js +186 -0
- package/build/_collections/utils/extract-error-message.util.js.map +1 -0
- package/build/_collections/utils/require-env.util.d.ts +86 -0
- package/build/_collections/utils/require-env.util.d.ts.map +1 -0
- package/build/_collections/utils/require-env.util.js +94 -0
- package/build/_collections/utils/require-env.util.js.map +1 -0
- package/build/_models/interfaces/environment/global-settings.interface.d.ts +22 -0
- package/build/_models/interfaces/environment/global-settings.interface.d.ts.map +1 -1
- package/build/index.d.ts +2 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/package.json +2 -2
- package/src/_collections/utils/extract-error-message.util.spec.ts +204 -0
- package/src/_collections/utils/extract-error-message.util.ts +223 -0
- package/src/_collections/utils/object.util.spec.ts +1 -1
- package/src/_collections/utils/require-env.util.spec.ts +231 -0
- package/src/_collections/utils/require-env.util.ts +138 -0
- package/src/_models/interfaces/environment/global-settings.interface.ts +23 -0
- package/src/index.ts +2 -0
- package/src/_collections/utils/math/box-bounds.util.spec.ts +0 -124
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { DyFM_Error } from '../../_models/control-models/error.control-model';
|
|
2
|
+
import { DyFM_global_settings } from '../constants/global-settings.const';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options for `DyFM_requireEnv`. See the helper's JSDoc for usage.
|
|
7
|
+
*/
|
|
8
|
+
export interface DyFM_RequireEnv_Options {
|
|
9
|
+
/**
|
|
10
|
+
* Stable error code emitted on the DyFM_Error when the env-var is missing
|
|
11
|
+
* and no fallback is configured. Convention: `<PACKAGE>-ENV-MISSING-<KEY>`,
|
|
12
|
+
* e.g. `DyNM-ENV-MISSING-STORAGE-KEY`, `FDP-ENV-MISSING-AUTH-KEY`.
|
|
13
|
+
*/
|
|
14
|
+
errorCode: string;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Short-code name of the package responsible for the env-var. Used as the
|
|
18
|
+
* DyFM_Error `___issuerService` for downstream filtering / dashboards.
|
|
19
|
+
*/
|
|
20
|
+
issuerService: string;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Transition fallback. When set, returned if BOTH `process.env[name]` AND
|
|
24
|
+
* `DyFM_global_settings.envOverrides[name]` are missing.
|
|
25
|
+
*
|
|
26
|
+
* Use only during the additive phase of an env-var migration to keep the
|
|
27
|
+
* package backward-compatible while every deploy target rolls the new
|
|
28
|
+
* env-var out. Remove the `fallback` argument once the rollout is verified
|
|
29
|
+
* everywhere — then a missing env-var fail-fast crashes the consumer at
|
|
30
|
+
* first access with a structured DyFM_Error.
|
|
31
|
+
*/
|
|
32
|
+
fallback?: string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Admin-actionable user-facing message attached to the DyFM_Error. Defaults
|
|
36
|
+
* to a generic config-missing string referencing the env-var name.
|
|
37
|
+
*/
|
|
38
|
+
userMessage?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* FR-015 Phase 2 — isomorphic env-var requirement helper.
|
|
44
|
+
*
|
|
45
|
+
* Universal source of truth for "read this configuration key from an env-var
|
|
46
|
+
* across Node and browser". Replaces per-package `requireEnv` helpers (the
|
|
47
|
+
* Phase 1 local implementation in fdp-templates-nts is being refactored to
|
|
48
|
+
* delegate here).
|
|
49
|
+
*
|
|
50
|
+
* Lookup order:
|
|
51
|
+
* 1. `process.env[envVarName]` — Node-side (server const files at module load).
|
|
52
|
+
* 2. `DyFM_global_settings.envOverrides[envVarName]` — browser-side, populated
|
|
53
|
+
* by the consumer at app bootstrap from its `environment.ts`. See
|
|
54
|
+
* DyFM_Global_Settings.envOverrides docs for the pattern.
|
|
55
|
+
* 3. `options.fallback` if provided — used during migration's additive phase.
|
|
56
|
+
* 4. throw a structured DyFM_Error with the full deploy-target checklist.
|
|
57
|
+
*
|
|
58
|
+
* The throw shape is rich on purpose (per the rich-error rule):
|
|
59
|
+
* - `___status` = 500 (configuration error)
|
|
60
|
+
* - `_errorCode` = caller-supplied stable code
|
|
61
|
+
* - `_message` = debug-level description with the env-var name + deploy-target
|
|
62
|
+
* checklist (host .env, docker-compose env-block, Overseer secrets-store,
|
|
63
|
+
* client environment.ts → bootstrap envOverrides)
|
|
64
|
+
* - `__userMessage` = admin-actionable message
|
|
65
|
+
* - `___issuerService` = caller-supplied package short-code
|
|
66
|
+
*
|
|
67
|
+
* @example Node server const file (module load):
|
|
68
|
+
* ```ts
|
|
69
|
+
* import { DyFM_requireEnv } from '@futdevpro/fsm-dynamo';
|
|
70
|
+
*
|
|
71
|
+
* export const FDP_keysEnv_settingsBase = {
|
|
72
|
+
* authKey: DyFM_requireEnv('FDP_AUTH_KEY', {
|
|
73
|
+
* errorCode: 'FDP-ENV-MISSING-AUTH-KEY',
|
|
74
|
+
* issuerService: 'fdp-templates',
|
|
75
|
+
* fallback: '<inherit literal>', // remove for Wave 3
|
|
76
|
+
* }),
|
|
77
|
+
* };
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* @example Browser-side lazy getter (deferred to first access, after consumer
|
|
81
|
+
* bootstrap has populated DyFM_global_settings.envOverrides):
|
|
82
|
+
* ```ts
|
|
83
|
+
* let _cached: string | undefined;
|
|
84
|
+
* function _resolveKey(): string {
|
|
85
|
+
* if (_cached !== undefined) return _cached;
|
|
86
|
+
* _cached = DyFM_requireEnv('DyNM_STORAGE_ENCRYPTION_KEY', { ... });
|
|
87
|
+
* return _cached;
|
|
88
|
+
* }
|
|
89
|
+
* export const DyNM_global_settings = {
|
|
90
|
+
* get storageEncryptionKey(): string { return _resolveKey(); },
|
|
91
|
+
* };
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export function DyFM_requireEnv(
|
|
95
|
+
envVarName: string,
|
|
96
|
+
options: DyFM_RequireEnv_Options,
|
|
97
|
+
): string {
|
|
98
|
+
// Node path: process.env at module-load time. Guarded for browser bundles
|
|
99
|
+
// where `process` may be undefined or shimmed without runtime values.
|
|
100
|
+
if (typeof process !== 'undefined' && process.env && process.env[envVarName]) {
|
|
101
|
+
return process.env[envVarName] as string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Browser path: consumer-populated envOverrides map. Populated at app
|
|
105
|
+
// bootstrap from environment.ts — see DyFM_Global_Settings.envOverrides.
|
|
106
|
+
const override: string | undefined =
|
|
107
|
+
DyFM_global_settings.envOverrides?.[envVarName];
|
|
108
|
+
if (override) {
|
|
109
|
+
return override;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Transition fallback (Wave 1 of an FR-015-style migration).
|
|
113
|
+
if (options.fallback !== undefined) {
|
|
114
|
+
return options.fallback;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Missing — fail fast with a rich, debug-friendly DyFM_Error.
|
|
118
|
+
throw new DyFM_Error({
|
|
119
|
+
status: 500,
|
|
120
|
+
errorCode: options.errorCode,
|
|
121
|
+
message:
|
|
122
|
+
`Environment variable '${envVarName}' is required but was not found. ` +
|
|
123
|
+
`Checked: process.env.${envVarName} (Node) and ` +
|
|
124
|
+
`DyFM_global_settings.envOverrides['${envVarName}'] (browser). ` +
|
|
125
|
+
`Both unset, and no transition fallback configured. ` +
|
|
126
|
+
`Set '${envVarName}' on every deploy target before deploying this ` +
|
|
127
|
+
`package version: (1) host .env on every host running a backend, ` +
|
|
128
|
+
`(2) docker-compose env-block on every consumer service, ` +
|
|
129
|
+
`(3) Overseer secrets-store entry for CI builds, ` +
|
|
130
|
+
`(4) client environment.ts plus app-bootstrap copy into ` +
|
|
131
|
+
`DyFM_global_settings.envOverrides for any browser consumer.`,
|
|
132
|
+
userMessage:
|
|
133
|
+
options.userMessage
|
|
134
|
+
?? `Configuration error — environment variable '${envVarName}' is missing. `
|
|
135
|
+
+ `Contact the responsible operator to set it on this deploy target.`,
|
|
136
|
+
issuerService: options.issuerService,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
@@ -21,4 +21,27 @@ export interface DyFM_Global_Settings {
|
|
|
21
21
|
* this setting will set which logs will be shown
|
|
22
22
|
*/
|
|
23
23
|
log_settings: DyFM_GlobalLog_Settings;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* FR-015 Phase 2 — isomorphic env-var overrides surface.
|
|
27
|
+
*
|
|
28
|
+
* On Node, `DyFM_requireEnv(name, ...)` reads `process.env[name]` directly.
|
|
29
|
+
* On browser there is no `process.env` at runtime, so consumers populate
|
|
30
|
+
* this map at app bootstrap from `environment.ts`:
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* import { DyFM_global_settings } from '@futdevpro/fsm-dynamo';
|
|
34
|
+
* import { environment } from './environments/environment';
|
|
35
|
+
*
|
|
36
|
+
* DyFM_global_settings.envOverrides = {
|
|
37
|
+
* FDP_AUTH_KEY: environment.authKey,
|
|
38
|
+
* FDP_EXTRA_AUTH_STORAGE_KEY: environment.extraAuthStorageKey,
|
|
39
|
+
* DyNM_STORAGE_ENCRYPTION_KEY: environment.storageEncryptionKey,
|
|
40
|
+
* };
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* `DyFM_requireEnv` checks `process.env` first, then this map, then
|
|
44
|
+
* the caller's optional `fallback`, then throws a structured DyFM_Error.
|
|
45
|
+
*/
|
|
46
|
+
envOverrides?: Record<string, string>;
|
|
24
47
|
}
|
package/src/index.ts
CHANGED
|
@@ -11,10 +11,12 @@ export * from './_collections/constants/times.const';
|
|
|
11
11
|
export * from './_collections/utils/array.util';
|
|
12
12
|
export * from './_collections/utils/async.util';
|
|
13
13
|
export * from './_collections/utils/data.util';
|
|
14
|
+
export * from './_collections/utils/extract-error-message.util';
|
|
14
15
|
export * from './_collections/utils/json-error-helper.util';
|
|
15
16
|
export * from './_collections/utils/log.util';
|
|
16
17
|
export * from './_collections/utils/round-list.util';
|
|
17
18
|
export * from './_collections/utils/object.util';
|
|
19
|
+
export * from './_collections/utils/require-env.util';
|
|
18
20
|
export * from './_collections/utils/stack.util';
|
|
19
21
|
export * from './_collections/utils/string.util';
|
|
20
22
|
export * from './_collections/utils/string-case.util';
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { DyFM_Vector2 } from '../../../_models/interfaces/vector2.interface';
|
|
2
|
-
import { DyFM_BoxBounds_Util } from './box-bounds.util';
|
|
3
|
-
import { DyFM_Vector2_Util } from './vector2.util';
|
|
4
|
-
|
|
5
|
-
xdescribe('| DyFM_BoxBounds', () => {
|
|
6
|
-
let boxBounds: DyFM_BoxBounds_Util;
|
|
7
|
-
const mockPosition = new DyFM_Vector2_Util({ x: 0, y: 0 });
|
|
8
|
-
const mockSize = new DyFM_Vector2_Util({ x: 10, y: 10 });
|
|
9
|
-
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
boxBounds = new DyFM_BoxBounds_Util(mockPosition, mockSize);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('| should set position correctly ()', () => {
|
|
15
|
-
const newPosition = new DyFM_Vector2_Util({ x: 5, y: 5 });
|
|
16
|
-
|
|
17
|
-
boxBounds.pos = newPosition;
|
|
18
|
-
expect(boxBounds.pos).toEqual(newPosition);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('| should get position correctly', () => {
|
|
22
|
-
const expectedPosition = new DyFM_Vector2_Util({ x: 5, y: 5 });
|
|
23
|
-
|
|
24
|
-
expect(boxBounds.pos).toEqual(expectedPosition);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('| should set size correctly', () => {
|
|
28
|
-
const newSize = new DyFM_Vector2_Util({ x: 20, y: 20 });
|
|
29
|
-
|
|
30
|
-
boxBounds.size = newSize;
|
|
31
|
-
expect(boxBounds.size).toEqual(newSize);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('| should get size correctly', () => {
|
|
35
|
-
const expectedSize = new DyFM_Vector2_Util({ x: 20, y: 20 });
|
|
36
|
-
|
|
37
|
-
expect(boxBounds.size).toEqual(expectedSize);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
xit('| should calculate center correctly', () => {
|
|
41
|
-
const expectedCenter = new DyFM_Vector2_Util({ x: 10, y: 10 });
|
|
42
|
-
expect(boxBounds.center).toEqual(expectedCenter);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('| should calculate center margin correctly', () => {
|
|
46
|
-
const expectedCenterMargin = new DyFM_Vector2_Util({ x: -5, y: -5 });
|
|
47
|
-
expect(boxBounds.centerMargin).toEqual(expectedCenterMargin);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('| should return true for constructed method', () => {
|
|
51
|
-
expect(boxBounds.constructed()).toBe(true);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('| should throw error when newValues has undefined position', () => {
|
|
55
|
-
expect(() => {
|
|
56
|
-
boxBounds.newValues(undefined as any, { x: 10, y: 10 });
|
|
57
|
-
}).toThrow(Error);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('| should throw error when newValues has undefined size', () => {
|
|
61
|
-
expect(() => {
|
|
62
|
-
boxBounds.newValues({ x: 0, y: 0 }, undefined as any);
|
|
63
|
-
}).toThrow(Error);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('| should clone and return a new instance', () => {
|
|
67
|
-
const clonedBoxBounds = boxBounds.clone();
|
|
68
|
-
|
|
69
|
-
expect(clonedBoxBounds).toBeInstanceOf(DyFM_BoxBounds_Util);
|
|
70
|
-
expect(clonedBoxBounds).toEqual(boxBounds);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
it('| should initialize with given position and size', () => {
|
|
75
|
-
expect(boxBounds.pos.x).toBe(mockPosition.x);
|
|
76
|
-
expect(boxBounds.pos.y).toBe(mockPosition.y);
|
|
77
|
-
expect(boxBounds.size.x).toBe(mockSize.x);
|
|
78
|
-
expect(boxBounds.size.y).toBe(mockSize.y);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('| should calculate center correctly', () => {
|
|
82
|
-
const expectedCenter = DyFM_Vector2_Util.plus(mockPosition, DyFM_Vector2_Util.divide(mockSize, 2));
|
|
83
|
-
expect(boxBounds.center.x).toBe(expectedCenter.x);
|
|
84
|
-
expect(boxBounds.center.y).toBe(expectedCenter.y);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('| should update position and size with newValues method', () => {
|
|
88
|
-
const newPosition: DyFM_Vector2 = { x: 50, y: 60 };
|
|
89
|
-
const newSize: DyFM_Vector2 = { x: 70, y: 80 };
|
|
90
|
-
boxBounds.newValues(newPosition, newSize);
|
|
91
|
-
|
|
92
|
-
expect(boxBounds.pos.x).toBe(newPosition.x);
|
|
93
|
-
expect(boxBounds.pos.y).toBe(newPosition.y);
|
|
94
|
-
expect(boxBounds.size.x).toBe(newSize.x);
|
|
95
|
-
expect(boxBounds.size.y).toBe(newSize.y);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('| should throw error if newValues is called with undefined position', () => {
|
|
99
|
-
expect(() => boxBounds.newValues(undefined as any, mockSize)).toThrowError('new position is undefined!');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('| should throw error if newValues is called with undefined size', () => {
|
|
103
|
-
expect(() => boxBounds.newValues(mockPosition, undefined as any)).toThrowError('new size is undefined!');
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('| should correctly determine if a position is within bounds', () => {
|
|
107
|
-
const insidePosition: DyFM_Vector2 = { x: 20, y: 30 };
|
|
108
|
-
const outsidePosition: DyFM_Vector2 = { x: 100, y: 100 };
|
|
109
|
-
|
|
110
|
-
expect(boxBounds.bounds(insidePosition)).toBeTrue();
|
|
111
|
-
expect(boxBounds.bounds(outsidePosition)).toBeFalse();
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('| should clone itself correctly', () => {
|
|
115
|
-
const clone = boxBounds.clone();
|
|
116
|
-
expect(clone.pos.x).toBe(boxBounds.pos.x);
|
|
117
|
-
expect(clone.pos.y).toBe(boxBounds.pos.y);
|
|
118
|
-
expect(clone.size.x).toBe(boxBounds.size.x);
|
|
119
|
-
expect(clone.size.y).toBe(boxBounds.size.y);
|
|
120
|
-
expect(clone).not.toBe(boxBounds);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
});
|