@adaas/a-utils 0.1.29 → 0.1.31
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/LICENSE +1 -1
- package/dist/index.cjs +18 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +459 -21
- package/dist/index.d.ts +459 -21
- package/dist/index.mjs +18 -28
- package/dist/index.mjs.map +1 -1
- package/examples/A-Logger-examples.ts +101 -24
- package/package.json +5 -5
- package/src/index.ts +16 -0
- package/src/lib/A-Logger/A-Logger.component.ts +385 -70
- package/src/lib/A-Logger/A-Logger.constants.ts +13 -0
- package/src/lib/A-Logger/A-Logger.types.ts +12 -1
- package/src/lib/A-Logger/README.md +116 -12
- package/src/lib/A-Memory/A-Memory.component.ts +1 -4
- package/src/lib/A-Route/A-Route.entity.ts +136 -0
- package/src/lib/A-Route/A-Route.types.ts +1 -0
- package/src/lib/A-Signal/A-Signal.constants.ts +10 -0
- package/src/lib/A-Signal/A-Signal.error.ts +0 -0
- package/src/lib/A-Signal/A-Signal.types.ts +56 -0
- package/src/lib/A-Signal/components/A-SignalBus.component.ts +126 -0
- package/src/lib/A-Signal/context/A-SignalConfig.context.ts +83 -0
- package/src/lib/A-Signal/context/A-SignalState.context.ts +195 -0
- package/src/lib/A-Signal/entities/A-Signal.entity.ts +69 -0
- package/src/lib/A-Signal/entities/A-SignalVector.entity.ts +198 -0
- package/tests/A-Logger.test.ts +90 -5
- package/tests/A-Route.test.ts +34 -0
- package/tests/A-Signal.test.ts +133 -0
- package/examples/config.ts +0 -33
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { A_Context, A_Fragment, A_TYPES__Component_Constructor } from "@adaas/a-concept";
|
|
2
|
+
import { A_Signal } from "../entities/A-Signal.entity";
|
|
3
|
+
import { A_SignalVector } from "../entities/A-SignalVector.entity";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A_SignalState manages the latest state of all signals within a given scope.
|
|
7
|
+
*
|
|
8
|
+
* This class maintains a mapping between signal constructors and their most recently emitted values,
|
|
9
|
+
* providing a centralized state store for signal management within an application context.
|
|
10
|
+
*
|
|
11
|
+
* @template TSignalData - Union type of all possible signal data types that can be stored (must extend Record<string, any>)
|
|
12
|
+
*
|
|
13
|
+
* The generic ensures type safety by maintaining correspondence between:
|
|
14
|
+
* - Signal constructor types and their data types
|
|
15
|
+
* - Signal instances and their emitted value types
|
|
16
|
+
* - Vector structure and the data it contains
|
|
17
|
+
*/
|
|
18
|
+
export class A_SignalState<
|
|
19
|
+
TSignalData extends Record<string, any> = Record<string, any>
|
|
20
|
+
> extends A_Fragment {
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Internal map storing the relationship between signal constructors and their latest values
|
|
24
|
+
* Key: Signal constructor function
|
|
25
|
+
* Value: Latest emitted data from that signal type
|
|
26
|
+
*/
|
|
27
|
+
protected _state: Map<A_TYPES__Component_Constructor<A_Signal>, A_Signal> = new Map();
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Optional structure defining the ordered list of signal constructors
|
|
31
|
+
* Used for vector operations and initialization
|
|
32
|
+
*/
|
|
33
|
+
protected _structure: Array<A_TYPES__Component_Constructor<A_Signal>>;
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Gets the ordered structure of signal constructors
|
|
38
|
+
* @returns Array of signal constructors in their defined order
|
|
39
|
+
*/
|
|
40
|
+
get structure(): Array<A_TYPES__Component_Constructor<A_Signal>> {
|
|
41
|
+
return this._structure || [];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Creates a new A_SignalState instance
|
|
46
|
+
*
|
|
47
|
+
* @param structure - Optional array defining the ordered structure of signal constructors
|
|
48
|
+
* This structure is used for vector operations and determines the order
|
|
49
|
+
* in which signals are processed and serialized
|
|
50
|
+
*/
|
|
51
|
+
constructor(
|
|
52
|
+
structure: A_TYPES__Component_Constructor<A_Signal>[]
|
|
53
|
+
) {
|
|
54
|
+
super({ name: "A_SignalState" });
|
|
55
|
+
|
|
56
|
+
this._structure = structure;
|
|
57
|
+
|
|
58
|
+
// Initialize the state map with undefined values for each signal in the structure
|
|
59
|
+
// This ensures all expected signals have entries in the state map from the start
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Sets the latest value for a specific signal type
|
|
66
|
+
*
|
|
67
|
+
* @param signal - The signal constructor to associate the value with
|
|
68
|
+
* @param value - The data value emitted by the signal
|
|
69
|
+
*/
|
|
70
|
+
set(
|
|
71
|
+
signal: A_Signal,
|
|
72
|
+
value: A_Signal
|
|
73
|
+
): void
|
|
74
|
+
set(
|
|
75
|
+
signal: A_Signal
|
|
76
|
+
): void
|
|
77
|
+
set(
|
|
78
|
+
signal: A_TYPES__Component_Constructor<A_Signal>,
|
|
79
|
+
value: A_Signal
|
|
80
|
+
): void
|
|
81
|
+
set(
|
|
82
|
+
param1: A_TYPES__Component_Constructor<A_Signal> | A_Signal,
|
|
83
|
+
param2?: A_Signal
|
|
84
|
+
): void {
|
|
85
|
+
const signal = param1 instanceof A_Signal ? param1.constructor as A_TYPES__Component_Constructor<A_Signal> : param1;
|
|
86
|
+
const value = param1 instanceof A_Signal ? param1 : param2!;
|
|
87
|
+
|
|
88
|
+
this._state.set(signal, value);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Retrieves the latest value for a specific signal type
|
|
93
|
+
*
|
|
94
|
+
* @param signal - The signal constructor to get the value for
|
|
95
|
+
* @returns The latest data value or undefined if no value has been set
|
|
96
|
+
*/
|
|
97
|
+
get(
|
|
98
|
+
signal: A_Signal
|
|
99
|
+
): A_Signal | undefined
|
|
100
|
+
get(
|
|
101
|
+
signal: A_TYPES__Component_Constructor<A_Signal>
|
|
102
|
+
): A_Signal | undefined
|
|
103
|
+
get(
|
|
104
|
+
param: A_TYPES__Component_Constructor<A_Signal> | A_Signal
|
|
105
|
+
): A_Signal | undefined {
|
|
106
|
+
const signal = param instanceof A_Signal ? param.constructor as A_TYPES__Component_Constructor<A_Signal> : param;
|
|
107
|
+
return this._state.get(signal);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Checks if a signal type has been registered in the state
|
|
112
|
+
*
|
|
113
|
+
* @param signal - The signal constructor to check for
|
|
114
|
+
* @returns True if the signal type exists in the state map
|
|
115
|
+
*/
|
|
116
|
+
has(
|
|
117
|
+
signal: A_Signal
|
|
118
|
+
): boolean
|
|
119
|
+
has(
|
|
120
|
+
signal: A_TYPES__Component_Constructor<A_Signal>
|
|
121
|
+
): boolean
|
|
122
|
+
has(
|
|
123
|
+
param: A_TYPES__Component_Constructor<A_Signal> | A_Signal
|
|
124
|
+
): boolean {
|
|
125
|
+
const signal = param instanceof A_Signal ? param.constructor as A_TYPES__Component_Constructor<A_Signal> : param;
|
|
126
|
+
|
|
127
|
+
return this.structure.includes(signal);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Removes a signal type and its associated value from the state
|
|
132
|
+
*
|
|
133
|
+
* @param signal - The signal constructor to remove
|
|
134
|
+
* @returns True if the signal was successfully deleted, false if it didn't exist
|
|
135
|
+
*/
|
|
136
|
+
delete(
|
|
137
|
+
signal: A_Signal
|
|
138
|
+
): boolean
|
|
139
|
+
delete(
|
|
140
|
+
signal: A_TYPES__Component_Constructor<A_Signal>
|
|
141
|
+
): boolean
|
|
142
|
+
delete(
|
|
143
|
+
param: A_TYPES__Component_Constructor<A_Signal> | A_Signal
|
|
144
|
+
): boolean {
|
|
145
|
+
const signal = param instanceof A_Signal ? param.constructor as A_TYPES__Component_Constructor<A_Signal> : param;
|
|
146
|
+
return this._state.delete(signal);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Converts the current state to a vector (ordered array) format
|
|
152
|
+
*
|
|
153
|
+
* The order is determined by the structure array provided during construction.
|
|
154
|
+
* Each position in the vector corresponds to a specific signal type's latest value.
|
|
155
|
+
*
|
|
156
|
+
* @returns Array of signal values in the order defined by the structure
|
|
157
|
+
* @throws Error if structure is not defined or if any signal value is undefined
|
|
158
|
+
*/
|
|
159
|
+
toVector(): A_SignalVector {
|
|
160
|
+
const vector: Array<A_Signal> = [];
|
|
161
|
+
|
|
162
|
+
this._state.forEach((value, key) => {
|
|
163
|
+
vector.push(value);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
return new A_SignalVector({
|
|
167
|
+
structure: this.structure,
|
|
168
|
+
values: vector
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Converts the current state to an object with signal constructor names as keys
|
|
174
|
+
*
|
|
175
|
+
* This provides a more readable representation of the state where each signal
|
|
176
|
+
* type is identified by its constructor name.
|
|
177
|
+
*
|
|
178
|
+
* @returns Object mapping signal constructor names to their latest values
|
|
179
|
+
* @throws Error if any signal value is undefined
|
|
180
|
+
*/
|
|
181
|
+
toObject(): Record<string, A_Signal> {
|
|
182
|
+
const obj: Record<string, A_Signal> = {};
|
|
183
|
+
|
|
184
|
+
this.structure.forEach((signalConstructor) => {
|
|
185
|
+
const value = this._state.get(signalConstructor);
|
|
186
|
+
if (value === undefined) {
|
|
187
|
+
throw new Error(`Signal ${signalConstructor.name} has no value in state`);
|
|
188
|
+
}
|
|
189
|
+
obj[signalConstructor.name] = value;
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
return obj;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { A_Entity, A_Scope } from "@adaas/a-concept";
|
|
2
|
+
import { A_Signal_Init, A_Signal_Serialized } from "../A-Signal.types";
|
|
3
|
+
import { A_SignalFeatures } from "../A-Signal.constants";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A Signal Entity is an individual signal instance that carries data.
|
|
7
|
+
* Signals is a event types that uses for vectors of signals to be used for further processing.
|
|
8
|
+
*
|
|
9
|
+
* Comparing to standard events, signals should be used in case when the event impacts some "state"
|
|
10
|
+
* and the state should be used instead of the event itself.
|
|
11
|
+
*
|
|
12
|
+
* For example, a signal can represent the current status of a user (online/offline/away),
|
|
13
|
+
* while an event would represent a single action (user logged in/logged out).
|
|
14
|
+
*
|
|
15
|
+
* Signals are typically used in scenarios where the current state is more important than individual events,
|
|
16
|
+
* such as monitoring systems, real-time dashboards, or stateful applications.
|
|
17
|
+
*/
|
|
18
|
+
export class A_Signal<
|
|
19
|
+
_TSignalDataType extends Record<string, any> = Record<string, any>
|
|
20
|
+
> extends A_Entity<A_Signal_Init<_TSignalDataType>, A_Signal_Serialized<_TSignalDataType>> {
|
|
21
|
+
|
|
22
|
+
data!: _TSignalDataType;
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Allows to define default data for the signal.
|
|
27
|
+
*
|
|
28
|
+
* If no data is provided during initialization, the default data will be used.
|
|
29
|
+
*
|
|
30
|
+
* @returns
|
|
31
|
+
*/
|
|
32
|
+
static async default(): Promise<A_Signal | undefined> {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
fromJSON(serializedEntity: A_Signal_Serialized<_TSignalDataType>): void {
|
|
39
|
+
super.fromJSON(serializedEntity);
|
|
40
|
+
this.data = serializedEntity.data;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
fromNew(newEntity: A_Signal_Init<_TSignalDataType>): void {
|
|
45
|
+
this.aseid = this.generateASEID({ entity: newEntity.name });
|
|
46
|
+
this.data = newEntity.data;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Emits this signal within the provided scope.
|
|
51
|
+
*
|
|
52
|
+
* Scope is mandatory since signal itself should not be registered in the scope,
|
|
53
|
+
* but should use particular scope context to use proper set of components
|
|
54
|
+
*
|
|
55
|
+
* @param scope
|
|
56
|
+
*/
|
|
57
|
+
async emit(scope: A_Scope) {
|
|
58
|
+
await this.call(A_SignalFeatures.Emit, scope);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
toJSON(): A_Signal_Serialized<_TSignalDataType> {
|
|
63
|
+
return {
|
|
64
|
+
...super.toJSON(),
|
|
65
|
+
data: this.data
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { A_Entity, A_TYPES__Component_Constructor, A_TYPES__Entity_Constructor } from "@adaas/a-concept";
|
|
2
|
+
import { A_SignalVector_Serialized, A_SignalVector_Init } from "../A-Signal.types";
|
|
3
|
+
import { A_Signal } from "./A-Signal.entity";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A Signal Vector Entity is a collection of signals structured in a specific way.
|
|
8
|
+
* It allows grouping multiple signals together for batch processing or transmission.
|
|
9
|
+
*
|
|
10
|
+
* Signal Vectors are useful in scenarios where multiple related signals need to be handled together,
|
|
11
|
+
* as a state of the system or a snapshot of various parameters at a given time.
|
|
12
|
+
*
|
|
13
|
+
* @template TSignalsConstructors - Array of signal constructor types (e.g., [typeof MySignal, typeof CustomSignal])
|
|
14
|
+
* @template TSignals - Array of signal instances derived from constructors
|
|
15
|
+
*/
|
|
16
|
+
export class A_SignalVector<
|
|
17
|
+
TSignals extends Array<A_Signal> = Array<A_Signal>,
|
|
18
|
+
TSignalsConstructors extends Array<A_TYPES__Entity_Constructor<A_Signal>> = TSignals extends Array<infer U> ? U extends A_Signal ? A_TYPES__Entity_Constructor<U>[] : never : never
|
|
19
|
+
> extends A_Entity<A_SignalVector_Init<TSignals[number][], TSignalsConstructors>, A_SignalVector_Serialized> {
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The structure of the signal vector, defining the types of signals it contains.
|
|
23
|
+
*
|
|
24
|
+
* For example:
|
|
25
|
+
* [UserSignInSignal, UserStatusSignal, UserActivitySignal]
|
|
26
|
+
*
|
|
27
|
+
* [!] if not provided, it will be derived from the signals values.
|
|
28
|
+
*/
|
|
29
|
+
protected _structure?: TSignalsConstructors;
|
|
30
|
+
/**
|
|
31
|
+
* It's actual vector Values of Signals like :
|
|
32
|
+
* [UserActionSignal, UserMousePositionSignal, ExternalDependencySignal]
|
|
33
|
+
*/
|
|
34
|
+
protected _signals!: TSignals[number][]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
fromNew(newEntity: A_SignalVector_Init<TSignals[number][], TSignalsConstructors>): void {
|
|
38
|
+
super.fromNew(newEntity);
|
|
39
|
+
this._structure = newEntity.structure;
|
|
40
|
+
this._signals = newEntity.values;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The structure of the signal vector, defining the types of signals it contains.
|
|
45
|
+
*
|
|
46
|
+
* For example:
|
|
47
|
+
* [UserSignInSignal, UserStatusSignal, UserActivitySignal]
|
|
48
|
+
*
|
|
49
|
+
*/
|
|
50
|
+
get structure(): TSignalsConstructors {
|
|
51
|
+
return this._structure || this._signals.map(s => s.constructor as A_TYPES__Entity_Constructor<A_Signal>) as TSignalsConstructors;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
get length(): number {
|
|
56
|
+
return this.structure.length;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Checks if the vector contains a signal of the specified type.
|
|
62
|
+
*
|
|
63
|
+
* @param signal
|
|
64
|
+
*/
|
|
65
|
+
has(signal: A_Signal): boolean
|
|
66
|
+
has(signalConstructor: A_TYPES__Component_Constructor<A_Signal>): boolean
|
|
67
|
+
has(param1: A_Signal | A_TYPES__Component_Constructor<A_Signal>): boolean {
|
|
68
|
+
let signalConstructor: A_TYPES__Component_Constructor<A_Signal>;
|
|
69
|
+
if (param1 instanceof A_Entity) {
|
|
70
|
+
signalConstructor = param1.constructor as A_TYPES__Component_Constructor<A_Signal>;
|
|
71
|
+
} else {
|
|
72
|
+
signalConstructor = param1;
|
|
73
|
+
}
|
|
74
|
+
return this.structure.includes(signalConstructor);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
get(signal: A_Signal): Record<string, any> | undefined
|
|
78
|
+
get(signalConstructor: A_TYPES__Component_Constructor<A_Signal>): Record<string, any> | undefined
|
|
79
|
+
get(param1: A_Signal | A_TYPES__Component_Constructor<A_Signal>): Record<string, any> | undefined {
|
|
80
|
+
let signalConstructor: A_TYPES__Component_Constructor<A_Signal>;
|
|
81
|
+
|
|
82
|
+
if (param1 instanceof A_Entity) {
|
|
83
|
+
signalConstructor = param1.constructor as A_TYPES__Component_Constructor<A_Signal>;
|
|
84
|
+
} else {
|
|
85
|
+
signalConstructor = param1;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const index = this._signals.findIndex(s => s.constructor === signalConstructor);
|
|
89
|
+
if (index === -1) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
return this._signals[index];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Converts to Array of values of signals in the vector
|
|
98
|
+
* Maintains the order specified in the structure/generic type
|
|
99
|
+
*
|
|
100
|
+
* @param structure - Optional structure to override the default ordering
|
|
101
|
+
* @returns Array of signal instances in the specified order
|
|
102
|
+
*/
|
|
103
|
+
async toVector<
|
|
104
|
+
T extends Array<A_Signal> = TSignals,
|
|
105
|
+
>(
|
|
106
|
+
structure?: { [K in keyof T]: T[K] extends A_Signal ? A_TYPES__Entity_Constructor<T[K]> : never }
|
|
107
|
+
): Promise<{ [K in keyof T]: T[K] }> {
|
|
108
|
+
const usedStructure = structure || this.structure;
|
|
109
|
+
|
|
110
|
+
return usedStructure.map((signalConstructor) => {
|
|
111
|
+
const signalIndex = this._signals.findIndex(s => s.constructor === signalConstructor);
|
|
112
|
+
return signalIndex !== -1 ? this._signals[signalIndex] : undefined;
|
|
113
|
+
}) as { [K in keyof T]: T[K] };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Converts to Array of data of signals in the vector
|
|
119
|
+
* Maintains the order specified in the structure/generic type
|
|
120
|
+
*
|
|
121
|
+
* @param structure - Optional structure to override the default ordering
|
|
122
|
+
* @returns Array of serialized signal data in the specified order
|
|
123
|
+
*/
|
|
124
|
+
async toDataVector<
|
|
125
|
+
T extends Array<A_Signal> = TSignals,
|
|
126
|
+
>(
|
|
127
|
+
structure?: { [K in keyof T]: T[K] extends A_Signal ? A_TYPES__Entity_Constructor<T[K]> : never }
|
|
128
|
+
): Promise<{ [K in keyof T]: T[K] extends A_Signal<infer D> ? D | undefined : never }> {
|
|
129
|
+
|
|
130
|
+
const usedStructure = structure || this.structure;
|
|
131
|
+
|
|
132
|
+
const results: Array<any> = [];
|
|
133
|
+
|
|
134
|
+
for (const signalConstructor of usedStructure) {
|
|
135
|
+
const signalIndex = this._signals.findIndex(s => s.constructor === signalConstructor);
|
|
136
|
+
let data: any;
|
|
137
|
+
if (signalIndex === -1) {
|
|
138
|
+
|
|
139
|
+
data = await (signalConstructor as typeof A_Signal).default()
|
|
140
|
+
|
|
141
|
+
} else {
|
|
142
|
+
const signal = this._signals[signalIndex];
|
|
143
|
+
data = signal;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
results.push(data?.toJSON().data);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return results as { [K in keyof T]: T[K] extends A_Signal<infer D> ? D | undefined : never };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Converts to Object with signal constructor names as keys and their corresponding data values
|
|
155
|
+
* Uses the structure ordering to ensure consistent key ordering
|
|
156
|
+
*
|
|
157
|
+
* @returns Object with signal constructor names as keys and signal data as values
|
|
158
|
+
*/
|
|
159
|
+
async toObject<
|
|
160
|
+
T extends Array<A_Signal> = TSignals,
|
|
161
|
+
>(
|
|
162
|
+
structure?: { [K in keyof T]: T[K] extends A_Signal ? A_TYPES__Entity_Constructor<T[K]> : never }
|
|
163
|
+
): Promise<{ [key: string]: T[number] extends A_Signal<infer D> ? D | undefined : never }> {
|
|
164
|
+
|
|
165
|
+
const usedStructure = structure || this.structure;
|
|
166
|
+
|
|
167
|
+
const obj: { [key: string]: T[number] extends A_Signal<infer D> ? D | undefined : never } = {};
|
|
168
|
+
usedStructure.forEach((signalConstructor) => {
|
|
169
|
+
const signalName = signalConstructor.name;
|
|
170
|
+
const signalIndex = this._signals.findIndex(s => s.constructor === signalConstructor);
|
|
171
|
+
|
|
172
|
+
if (signalIndex !== -1) {
|
|
173
|
+
const signal = this._signals[signalIndex];
|
|
174
|
+
obj[signalName] = signal.toJSON().data as any;
|
|
175
|
+
} else {
|
|
176
|
+
obj[signalName] = undefined as any;
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
return obj;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Serializes the Signal Vector to a JSON-compatible format.
|
|
186
|
+
*
|
|
187
|
+
*
|
|
188
|
+
* @returns
|
|
189
|
+
*/
|
|
190
|
+
toJSON(): A_SignalVector_Serialized {
|
|
191
|
+
return {
|
|
192
|
+
...super.toJSON(),
|
|
193
|
+
structure: this.structure.map(s => s.name),
|
|
194
|
+
values: this._signals.map(s => s.toJSON())
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
package/tests/A-Logger.test.ts
CHANGED
|
@@ -215,7 +215,8 @@ describe('A_Logger Component', () => {
|
|
|
215
215
|
|
|
216
216
|
const logs = getCapturedLogs();
|
|
217
217
|
expect(logs[0]).toContain('Test error message');
|
|
218
|
-
expect(logs[0]).toContain('
|
|
218
|
+
expect(logs[0]).toContain('ERROR: Error');
|
|
219
|
+
expect(logs[0]).toContain('STACK TRACE:');
|
|
219
220
|
});
|
|
220
221
|
|
|
221
222
|
test('should format A_Error instances with special formatting', () => {
|
|
@@ -253,7 +254,7 @@ describe('A_Logger Component', () => {
|
|
|
253
254
|
|
|
254
255
|
colors.forEach(color => {
|
|
255
256
|
clearCapturedLogs();
|
|
256
|
-
logger.
|
|
257
|
+
logger.info(color, `${color} message`);
|
|
257
258
|
|
|
258
259
|
const logs = getCapturedLogs();
|
|
259
260
|
expect(logs[0]).toContain(`${color} message`);
|
|
@@ -261,6 +262,26 @@ describe('A_Logger Component', () => {
|
|
|
261
262
|
});
|
|
262
263
|
});
|
|
263
264
|
|
|
265
|
+
test('should support extended color palette from A_LoggerColorName enum', () => {
|
|
266
|
+
const extendedColors: Array<keyof typeof A_LOGGER_COLORS> = [
|
|
267
|
+
'brightBlue', 'brightCyan', 'brightMagenta',
|
|
268
|
+
'indigo', 'violet', 'purple', 'lavender',
|
|
269
|
+
'skyBlue', 'steelBlue', 'slateBlue', 'deepBlue',
|
|
270
|
+
'lightBlue', 'periwinkle', 'cornflower', 'powder',
|
|
271
|
+
'darkGray', 'lightGray', 'charcoal', 'silver', 'smoke', 'slate'
|
|
272
|
+
];
|
|
273
|
+
|
|
274
|
+
extendedColors.forEach(color => {
|
|
275
|
+
clearCapturedLogs();
|
|
276
|
+
logger.info(color, `Extended ${color} message`);
|
|
277
|
+
|
|
278
|
+
const logs = getCapturedLogs();
|
|
279
|
+
// Clean the output to handle line wrapping
|
|
280
|
+
const cleanedOutput = logs[0].replace(/\n\s*\|\s*/g, ' ').replace(/\s+/g, ' ');
|
|
281
|
+
expect(cleanedOutput).toContain(`Extended ${color} message`);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
264
285
|
test('should default to blue color when no color specified', () => {
|
|
265
286
|
logger.log('Default color message');
|
|
266
287
|
|
|
@@ -409,13 +430,19 @@ describe('A_Logger Component', () => {
|
|
|
409
430
|
expect(logs[0]).toContain('[Circular Reference]');
|
|
410
431
|
});
|
|
411
432
|
|
|
412
|
-
test('should handle very long strings', () => {
|
|
433
|
+
test('should handle very long strings with wrapping', () => {
|
|
413
434
|
const longString = 'A'.repeat(1000);
|
|
414
435
|
|
|
415
|
-
logger.
|
|
436
|
+
logger.info('Long string:', longString);
|
|
416
437
|
|
|
417
438
|
const logs = getCapturedLogs();
|
|
418
|
-
|
|
439
|
+
// The logger now wraps long strings, so we check for the presence of parts of the string
|
|
440
|
+
// rather than the entire string in one line
|
|
441
|
+
expect(logs[0]).toContain('Long string:');
|
|
442
|
+
expect(logs[0]).toContain('AAAAAAAAAAA'); // Should contain many A's
|
|
443
|
+
// The string should be wrapped across multiple lines
|
|
444
|
+
const logLines = logs[0].split('\n');
|
|
445
|
+
expect(logLines.length).toBeGreaterThan(5); // Should be wrapped into multiple lines
|
|
419
446
|
});
|
|
420
447
|
|
|
421
448
|
test('should handle empty scope name', () => {
|
|
@@ -466,6 +493,64 @@ describe('A_Logger Component', () => {
|
|
|
466
493
|
expect(endTime - startTime).toBeLessThan(500); // Should complete within 500ms
|
|
467
494
|
});
|
|
468
495
|
});
|
|
496
|
+
|
|
497
|
+
// =============================================
|
|
498
|
+
// Terminal Width and Formatting Tests
|
|
499
|
+
// =============================================
|
|
500
|
+
|
|
501
|
+
describe('Terminal Width Detection and Formatting', () => {
|
|
502
|
+
test('should detect terminal width in Node.js environment', () => {
|
|
503
|
+
// The logger should initialize without errors and handle terminal width detection
|
|
504
|
+
expect(() => {
|
|
505
|
+
const testScope = new A_Scope({ name: 'TerminalTest' });
|
|
506
|
+
const terminalLogger = new A_Logger(testScope);
|
|
507
|
+
terminalLogger.info('Terminal width test message');
|
|
508
|
+
}).not.toThrow();
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
test('should wrap long messages appropriately', () => {
|
|
512
|
+
// Create a message that's extremely long to ensure wrapping even on wide terminals
|
|
513
|
+
// The message needs to be longer than most reasonable terminal widths (200+ chars)
|
|
514
|
+
const extremelyLongMessage = 'This is an extraordinarily and exceptionally long message designed specifically for testing text wrapping functionality in the A_Logger component, containing numerous words and phrases that together form a sentence of considerable length that should definitely exceed the available width in most terminal configurations, thereby demonstrating the logger\'s sophisticated text wrapping capabilities and ensuring proper formatting across diverse environments with varying screen sizes and terminal window dimensions, while maintaining readability and professional presentation standards.';
|
|
515
|
+
|
|
516
|
+
logger.info('cyan', extremelyLongMessage);
|
|
517
|
+
|
|
518
|
+
const logs = getCapturedLogs();
|
|
519
|
+
|
|
520
|
+
// Remove all newlines and extra spaces to get the actual content for verification
|
|
521
|
+
const cleanedOutput = logs[0].replace(/\n\s*\|\s*/g, ' ').replace(/\s+/g, ' ');
|
|
522
|
+
|
|
523
|
+
// Verify the message content is preserved regardless of wrapping
|
|
524
|
+
expect(cleanedOutput).toContain('extraordinarily and exceptionally long message');
|
|
525
|
+
expect(cleanedOutput).toContain('readability and professional presentation');
|
|
526
|
+
|
|
527
|
+
// The message should either be wrapped with newlines OR be very long on one line
|
|
528
|
+
const logOutput = logs[0];
|
|
529
|
+
const hasWrapping = logOutput.includes('\n') && logOutput.includes('|'); // Continuation markers
|
|
530
|
+
const isSingleLongLine = logOutput.length > 300; // Very long single line
|
|
531
|
+
|
|
532
|
+
// In narrow terminals, wrapping occurs with continuation markers
|
|
533
|
+
// In wide terminals, the message appears as one very long line
|
|
534
|
+
expect(hasWrapping || isSingleLongLine).toBe(true);
|
|
535
|
+
|
|
536
|
+
// Ensure the complete message is present (checking cleaned version)
|
|
537
|
+
expect(cleanedOutput).toContain('extraordinarily and exceptionally long message');
|
|
538
|
+
expect(cleanedOutput).toContain('professional presentation standards');
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
test('should handle multi-argument messages with proper indentation', () => {
|
|
542
|
+
logger.info('brightMagenta',
|
|
543
|
+
'First long argument that might wrap across lines',
|
|
544
|
+
'Second argument for testing indentation',
|
|
545
|
+
{ complexObject: 'with nested data for formatting tests' }
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
const logs = getCapturedLogs();
|
|
549
|
+
expect(logs[0]).toContain('First long argument');
|
|
550
|
+
expect(logs[0]).toContain('Second argument');
|
|
551
|
+
expect(logs[0]).toContain('complexObject');
|
|
552
|
+
});
|
|
553
|
+
});
|
|
469
554
|
});
|
|
470
555
|
|
|
471
556
|
// =============================================
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { A_Route } from "@adaas/a-utils/lib/A-Route/A-Route.entity";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
jest.retryTimes(0);
|
|
7
|
+
|
|
8
|
+
describe('A-Route tests', () => {
|
|
9
|
+
it('Should Allow to create a new A-Route', async () => {
|
|
10
|
+
let route = new A_Route('/test/route');
|
|
11
|
+
|
|
12
|
+
expect(route).toBeInstanceOf(A_Route);
|
|
13
|
+
expect(route.path).toBe('/test/route');
|
|
14
|
+
});
|
|
15
|
+
it('Should properly parse and extract path params', async () => {
|
|
16
|
+
let route = new A_Route('/test/route/:param1/:param2');
|
|
17
|
+
|
|
18
|
+
expect(route).toBeInstanceOf(A_Route);
|
|
19
|
+
expect(route.path).toBe('/test/route/:param1/:param2');
|
|
20
|
+
expect(route.params).toEqual(['param1', 'param2']);
|
|
21
|
+
|
|
22
|
+
const extractedParams = route.extractParams('/test/route/value1/value2');
|
|
23
|
+
expect(extractedParams).toEqual({ param1: 'value1', param2: 'value2' });
|
|
24
|
+
});
|
|
25
|
+
it('Should properly parse received URL', async () => {
|
|
26
|
+
let route = new A_Route('https://example.com/test/route?param1=value1¶m2=value2');
|
|
27
|
+
|
|
28
|
+
expect(route).toBeInstanceOf(A_Route);
|
|
29
|
+
expect(route.path).toBe('/test/route');
|
|
30
|
+
const query = route.extractQuery('https://example.com/test/route?param1=value1¶m2=value2');
|
|
31
|
+
expect(query).toEqual({ param1: 'value1', param2: 'value2' });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
})
|