@adaas/a-utils 0.2.5 → 0.2.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adaas/a-utils",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "A-Utils is a set of utilities that are used across the ADAAS ecosystem. This package is designed to be a collection of utilities that are used across the ADAAS ecosystem.",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./dist/index.cjs",
@@ -80,8 +80,8 @@
80
80
  "build": "tsup --config tsup.config.ts"
81
81
  },
82
82
  "dependencies": {
83
- "@adaas/a-concept": "^0.2.5",
84
- "@adaas/a-frame": "^0.0.7"
83
+ "@adaas/a-concept": "^0.2.7",
84
+ "@adaas/a-frame": "^0.0.8"
85
85
  },
86
86
  "devDependencies": {
87
87
  "@types/chai": "^4.3.14",
@@ -2,13 +2,13 @@ import { A_TYPES__Component_Constructor, A_TYPES__Entity_Constructor, A_TYPES__E
2
2
  import { A_Signal } from "./entities/A-Signal.entity"
3
3
 
4
4
 
5
- export type A_SignalConfig_Init = {
5
+ export type A_SignalConfig_Init<TSignals extends Array<A_Signal> = Array<A_Signal>> = {
6
6
  /**
7
7
  * An array defining the structure of the signal vector.
8
8
  *
9
9
  * Each entry corresponds to a signal component constructor.
10
10
  */
11
- structure?: Array<A_TYPES__Entity_Constructor<A_Signal>>
11
+ structure?: A_Signal_TSignalsConstructors<TSignals>
12
12
  /**
13
13
  * A string representation of the structure for easier DI resolution.
14
14
  * Each signal's constructor name is used to form this string.
@@ -21,14 +21,25 @@ export type A_SignalConfig_Init = {
21
21
  propagateSignals?: boolean
22
22
  }
23
23
 
24
+ // export type A_Signal_TSignalsConstructors<T extends Array<A_Signal> = Array<A_Signal>> = {
25
+ // [K in keyof T]: T[K] extends A_Signal ? A_TYPES__Entity_Constructor<T[K]> : never
26
+ // }
27
+
28
+ export type A_Signal_TSignalsConstructors<T extends Array<A_Signal> = Array<A_Signal>> = T extends Array<infer U> ? U extends A_Signal ? A_TYPES__Entity_Constructor<U>[] : never : never
29
+
30
+
31
+ export type A_SignalTValue<T extends A_Signal> = T extends A_Signal<infer U> ? U : never;
32
+
33
+ export type A_SignalTValueArray<T extends Array<A_Signal>> = {
34
+ [K in keyof T]: T[K] extends A_Signal<infer U> ? U : never
35
+ }
24
36
 
25
37
 
26
38
  export type A_SignalVector_Init<
27
39
  _TSignals extends Array<A_Signal> = Array<A_Signal>,
28
- _TVectorStructure extends Array<A_TYPES__Entity_Constructor<_TSignals[number]>> = Array<A_TYPES__Entity_Constructor<_TSignals[number]>>
29
40
 
30
41
  > = {
31
- structure: _TVectorStructure,
42
+ structure: A_Signal_TSignalsConstructors<_TSignals>,
32
43
  values: _TSignals
33
44
  }
34
45
 
@@ -41,6 +52,12 @@ export type A_SignalVector_Serialized = A_TYPES__Entity_Serialized & {
41
52
 
42
53
 
43
54
  export type A_Signal_Init<T extends Record<string, any> = Record<string, any>> = {
55
+ /**
56
+ * Possible signal id
57
+ *
58
+ * [!] used for identification only, all properties will be participating in the hash calculation for deduplication
59
+ */
60
+ id?: Array<any>,
44
61
  /**
45
62
  * The signal name
46
63
  */
@@ -195,10 +195,7 @@ export class A_SignalState<
195
195
  vector.push(value);
196
196
  });
197
197
 
198
- return new A_SignalVector({
199
- structure: this.structure,
200
- values: vector
201
- });
198
+ return new A_SignalVector(vector, this.structure);
202
199
  }
203
200
 
204
201
  /**
@@ -24,9 +24,10 @@ export class A_Signal<
24
24
  _TSignalDataType extends Record<string, any> = Record<string, any>
25
25
  > extends A_Entity<A_Signal_Init<_TSignalDataType>, A_Signal_Serialized<_TSignalDataType>> {
26
26
 
27
- data!: _TSignalDataType;
28
-
29
27
 
28
+ // ========================================================================
29
+ // ========================== Static Methods ==============================
30
+ // ========================================================================
30
31
  /**
31
32
  * Allows to define default data for the signal.
32
33
  *
@@ -38,6 +39,99 @@ export class A_Signal<
38
39
  return undefined;
39
40
  }
40
41
 
42
+ // ========================================================================
43
+ // ========================== Instance Properties ========================
44
+ // ========================================================================
45
+
46
+ /**
47
+ * The actual data carried by the signal.
48
+ */
49
+ data!: _TSignalDataType;
50
+
51
+ /**
52
+ * Generates signal hash uses for comparison
53
+ *
54
+ * @param str
55
+ */
56
+ protected createHash(str?: string): string
57
+ protected createHash(str?: undefined): string
58
+ protected createHash(str?: Record<string, any>): string
59
+ protected createHash(str?: Array<any>): string
60
+ protected createHash(str?: number): string
61
+ protected createHash(str?: boolean): string
62
+ protected createHash(str?: null): string
63
+ protected createHash(map?: Map<any, any>): string
64
+ protected createHash(set?: Set<any>): string
65
+ protected createHash(str?: any): string {
66
+ let hashSource: string;
67
+
68
+ if (str instanceof Map) {
69
+ hashSource = JSON.stringify(Array.from(str.entries()));
70
+ } else if (str instanceof Set) {
71
+ hashSource = JSON.stringify(Array.from(str.values()));
72
+ } else {
73
+ switch (typeof str) {
74
+ case 'string':
75
+ hashSource = str;
76
+ break;
77
+ case 'undefined':
78
+ hashSource = 'undefined';
79
+ break;
80
+
81
+ case 'object':
82
+ if ('toJSON' in str)
83
+ hashSource = JSON.stringify(str.toJSON());
84
+
85
+ else
86
+ hashSource = JSON.stringify(str);
87
+ break;
88
+ case 'number':
89
+ hashSource = str.toString();
90
+ break;
91
+ case 'boolean':
92
+ hashSource = str ? 'true' : 'false';
93
+ break;
94
+ case 'function':
95
+ hashSource = str.toString();
96
+ break;
97
+ default:
98
+ hashSource = String(str);
99
+ }
100
+ }
101
+
102
+ let hash = 0, i, chr;
103
+ for (i = 0; i < hashSource.length; i++) {
104
+ chr = hashSource.charCodeAt(i);
105
+ hash = ((hash << 5) - hash) + chr;
106
+ hash |= 0; // Convert to 32bit integer
107
+ }
108
+
109
+ const hashString = hash.toString();
110
+
111
+ return hashString;
112
+ }
113
+
114
+ /**
115
+ * This method compares the current signal with another signal instance by deduplication ID
116
+ * this id can be configured during initialization with the "id" property.
117
+ *
118
+ * example:
119
+ * * const signalA = new A_Signal({ id: ['user-status', 'user123'], data: { status: 'online' } });
120
+ * * const signalB = new A_Signal({ id: ['user-status', 'user123'], data: { status: 'offline' } });
121
+ *
122
+ * signalA.compare(signalB) // true because both signals have the same deduplication ID
123
+ *
124
+ * @param other
125
+ * @returns
126
+ */
127
+ compare(other: A_Signal<_TSignalDataType>): boolean {
128
+ if (this.aseid.id !== other.aseid.id) {
129
+ return false;
130
+ }
131
+
132
+ return true;
133
+ }
134
+
41
135
 
42
136
 
43
137
  fromJSON(serializedEntity: A_Signal_Serialized<_TSignalDataType>): void {
@@ -47,10 +141,20 @@ export class A_Signal<
47
141
 
48
142
 
49
143
  fromNew(newEntity: A_Signal_Init<_TSignalDataType>): void {
50
- this.aseid = this.generateASEID({ entity: newEntity.name });
51
144
  this.data = newEntity.data;
52
- }
53
145
 
146
+ const identity = newEntity.id || {
147
+ name: newEntity.name,
148
+ data: this.data
149
+ };
150
+
151
+ const id = this.createHash(identity);
152
+
153
+ this.aseid = this.generateASEID({
154
+ entity: newEntity.name,
155
+ id: id,
156
+ });
157
+ }
54
158
 
55
159
 
56
160
  toJSON(): A_Signal_Serialized<_TSignalDataType> {
@@ -1,5 +1,5 @@
1
1
  import { A_Entity, A_Scope, A_TYPES__Component_Constructor, A_TYPES__Entity_Constructor } from "@adaas/a-concept";
2
- import { A_SignalVector_Serialized, A_SignalVector_Init } from "../A-Signal.types";
2
+ import { A_SignalVector_Serialized, A_SignalVector_Init, A_Signal_TSignalsConstructors, A_SignalTValue, A_SignalTValueArray } from "../A-Signal.types";
3
3
  import { A_Signal } from "./A-Signal.entity";
4
4
  import { A_Frame } from "@adaas/a-frame";
5
5
 
@@ -20,9 +20,8 @@ import { A_Frame } from "@adaas/a-frame";
20
20
  description: 'A Signal Vector Entity represents a collection of signals structured in a specific way, allowing for batch processing and transmission of related signals as a unified state representation.'
21
21
  })
22
22
  export class A_SignalVector<
23
- TSignals extends Array<A_Signal> = Array<A_Signal>,
24
- 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
25
- > extends A_Entity<A_SignalVector_Init<TSignals[number][], TSignalsConstructors>, A_SignalVector_Serialized> {
23
+ TSignals extends A_Signal[] = A_Signal[],
24
+ > extends A_Entity<A_SignalVector_Init<TSignals>> {
26
25
 
27
26
  /**
28
27
  * The structure of the signal vector, defining the types of signals it contains.
@@ -32,15 +31,31 @@ export class A_SignalVector<
32
31
  *
33
32
  * [!] if not provided, it will be derived from the signals values.
34
33
  */
35
- protected _structure?: TSignalsConstructors;
34
+ protected _structure?: A_Signal_TSignalsConstructors<TSignals>;
36
35
  /**
37
36
  * It's actual vector Values of Signals like :
38
37
  * [UserActionSignal, UserMousePositionSignal, ExternalDependencySignal]
39
38
  */
40
- protected _signals!: TSignals[number][]
39
+ protected _signals!: TSignals
41
40
 
41
+ constructor(values: TSignals, structure?: { [K in keyof TSignals]: TSignals[K] extends A_Signal ? A_TYPES__Entity_Constructor<TSignals[K]> : never })
42
+ constructor(serialized: A_SignalVector_Serialized)
43
+ constructor(param1: TSignals | A_SignalVector_Serialized, param2?: A_Signal_TSignalsConstructors<TSignals>) {
42
44
 
43
- fromNew(newEntity: A_SignalVector_Init<TSignals[number][], TSignalsConstructors>): void {
45
+ if ('aseid' in param1) {
46
+ // Handle serialized case
47
+ super(param1 as A_SignalVector_Serialized);
48
+ } else {
49
+ // Handle init case
50
+ super({
51
+ structure: param2 ? param2 : (param1 as TSignals).map(s => s.constructor as A_TYPES__Entity_Constructor<TSignals[number]>) as A_Signal_TSignalsConstructors<TSignals>,
52
+ values: param1 as TSignals
53
+ } as A_SignalVector_Init<TSignals>);
54
+ }
55
+ }
56
+
57
+
58
+ fromNew(newEntity: A_SignalVector_Init<TSignals>): void {
44
59
  super.fromNew(newEntity);
45
60
  this._structure = newEntity.structure;
46
61
  this._signals = newEntity.values;
@@ -53,8 +68,8 @@ export class A_SignalVector<
53
68
  * [UserSignInSignal, UserStatusSignal, UserActivitySignal]
54
69
  *
55
70
  */
56
- get structure(): TSignalsConstructors {
57
- return this._structure || this._signals.map(s => s.constructor as A_TYPES__Entity_Constructor<A_Signal>) as TSignalsConstructors;
71
+ get structure(): A_Signal_TSignalsConstructors<TSignals> {
72
+ return this._structure || this._signals.map(s => s.constructor as A_TYPES__Entity_Constructor<TSignals[number]>) as A_Signal_TSignalsConstructors<TSignals>;
58
73
  }
59
74
 
60
75
 
@@ -93,22 +108,75 @@ export class A_SignalVector<
93
108
  }
94
109
 
95
110
 
111
+ /**
112
+ * Allows to match the current Signal Vector with another Signal Vector by comparing each signal in the structure.
113
+ * This method returns true if all signals in the vector match the corresponding signals in the other vector.
114
+ *
115
+ * @param other
116
+ * @returns
117
+ */
118
+ match(other: A_SignalVector<TSignals>): boolean {
119
+ if (this.length !== other.length) {
120
+ return false;
121
+ }
122
+
123
+ for (let i = 0; i < this.length; i++) {
124
+ const thisSignalConstructor = this.structure[i];
125
+ const otherSignalConstructor = other.structure[i];
126
+
127
+ if (thisSignalConstructor !== otherSignalConstructor) {
128
+ return false;
129
+ }
130
+
131
+ const thisSignalIndex = this._signals.findIndex(s => s.constructor === thisSignalConstructor);
132
+ const otherSignalIndex = other._signals.findIndex(s => s.constructor === otherSignalConstructor);
133
+
134
+ const thisSignal = thisSignalIndex !== -1 ? this._signals[thisSignalIndex] : undefined;
135
+ const otherSignal = otherSignalIndex !== -1 ? other._signals[otherSignalIndex] : undefined;
136
+
137
+ if (thisSignal && otherSignal) {
138
+ if (!thisSignal.compare(otherSignal)) {
139
+ return false;
140
+ }
141
+ } else if (thisSignal || otherSignal) {
142
+ return false;
143
+ }
144
+ }
145
+
146
+ return true;
147
+ }
148
+
96
149
 
150
+ /**
151
+ * This method should ensure that the current Signal Vector contains all signals from the provided Signal Vector.
152
+ *
153
+ * @param signal
154
+ */
155
+ contains(signal: A_SignalVector): boolean{
156
+ for (const signalConstructor of signal.structure) {
157
+ const signalIndex = this._signals.findIndex(s => s.constructor === signalConstructor);
158
+ if (signalIndex === -1) {
159
+ return false;
160
+ }
161
+ }
162
+ return true;
163
+ }
164
+
97
165
  /**
98
166
  * Checks if the vector contains a signal of the specified type.
99
167
  *
100
168
  * @param signal
101
169
  */
102
170
  has(signal: A_Signal): boolean
103
- has(signalConstructor: A_TYPES__Component_Constructor<A_Signal>): boolean
104
- has(param1: A_Signal | A_TYPES__Component_Constructor<A_Signal>): boolean {
105
- let signalConstructor: A_TYPES__Component_Constructor<A_Signal>;
171
+ has(signalConstructor: A_TYPES__Entity_Constructor<A_Signal>): boolean
172
+ has(param1: A_Signal | A_TYPES__Entity_Constructor<A_Signal>): boolean {
173
+ let signalConstructor: A_TYPES__Entity_Constructor<A_Signal>;
106
174
  if (param1 instanceof A_Entity) {
107
- signalConstructor = param1.constructor as A_TYPES__Component_Constructor<A_Signal>;
175
+ signalConstructor = param1.constructor as A_TYPES__Entity_Constructor<A_Signal>;
108
176
  } else {
109
177
  signalConstructor = param1;
110
178
  }
111
- return this.structure.includes(signalConstructor);
179
+ return this.structure.includes(signalConstructor as any);
112
180
  }
113
181
 
114
182
  /**
@@ -145,14 +213,14 @@ export class A_SignalVector<
145
213
  async toVector<
146
214
  T extends Array<A_Signal> = TSignals,
147
215
  >(
148
- structure?: { [K in keyof T]: T[K] extends A_Signal ? A_TYPES__Entity_Constructor<T[K]> : never }
149
- ): Promise<{ [K in keyof T]: T[K] }> {
216
+ structure?: A_Signal_TSignalsConstructors<T>
217
+ ): Promise<T> {
150
218
  const usedStructure = structure || this.structure;
151
219
 
152
220
  return usedStructure.map((signalConstructor) => {
153
221
  const signalIndex = this._signals.findIndex(s => s.constructor === signalConstructor);
154
222
  return signalIndex !== -1 ? this._signals[signalIndex] : undefined;
155
- }) as { [K in keyof T]: T[K] };
223
+ }) as T;
156
224
  }
157
225
 
158
226
 
@@ -164,10 +232,10 @@ export class A_SignalVector<
164
232
  * @returns Array of serialized signal data in the specified order
165
233
  */
166
234
  async toDataVector<
167
- T extends Array<A_Signal> = TSignals,
235
+ T extends A_Signal[] = TSignals,
168
236
  >(
169
- structure?: { [K in keyof T]: T[K] extends A_Signal ? A_TYPES__Entity_Constructor<T[K]> : never }
170
- ): Promise<{ [K in keyof T]: T[K] extends A_Signal<infer D> ? D | undefined : never }> {
237
+ structure?: A_Signal_TSignalsConstructors<T>
238
+ ): Promise<A_SignalTValueArray<T>> {
171
239
 
172
240
  const usedStructure = structure || this.structure;
173
241
 
@@ -189,7 +257,7 @@ export class A_SignalVector<
189
257
  results.push(data?.toJSON().data);
190
258
  }
191
259
 
192
- return results as { [K in keyof T]: T[K] extends A_Signal<infer D> ? D | undefined : never };
260
+ return results as A_SignalTValueArray<T>;
193
261
  }
194
262
 
195
263
  /**
@@ -61,7 +61,23 @@ export class A_StateMachine<
61
61
  */
62
62
  get ready(): Promise<void> {
63
63
  if (!this._initialized) {
64
- this._initialized = this.call(A_StateMachineFeatures.onInitialize);
64
+ this._initialized = new Promise<void>(
65
+ async (resolve, reject) => {
66
+ try {
67
+ await this.call(A_StateMachineFeatures.onInitialize);
68
+
69
+ resolve();
70
+ } catch (error) {
71
+ const wrappedError = new A_StateMachineError({
72
+ title: A_StateMachineError.InitializationError,
73
+ description: `An error occurred during state machine initialization.`,
74
+ originalError: error
75
+ });
76
+
77
+ reject(wrappedError);
78
+ }
79
+ }
80
+ );
65
81
  }
66
82
 
67
83
  return this._initialized;
@@ -25,13 +25,10 @@ describe('A-Signal tests', () => {
25
25
  class MySignalA extends A_Signal<{ buttonId: string }> { }
26
26
  class MySignalB extends A_Signal<{ pageId: string }> { }
27
27
 
28
- const vector = new A_SignalVector({
29
- structure: [MySignalA, MySignalB],
30
- values: [
31
- new MySignalA({ data: { buttonId: 'submit-order' } }),
32
- new MySignalB({ data: { pageId: 'home-page' } })
33
- ]
34
- });
28
+ const vector = new A_SignalVector<[MySignalA, MySignalB]>([
29
+ new MySignalA({ data: { buttonId: 'submit-order' } }),
30
+ new MySignalB({ data: { pageId: 'home-page' } })
31
+ ]);
35
32
 
36
33
  expect(vector).toBeDefined();
37
34
  expect(vector).toBeInstanceOf(A_SignalVector);
@@ -39,18 +36,59 @@ describe('A-Signal tests', () => {
39
36
  expect((await vector.toDataVector())[0]?.buttonId).toBe('submit-order');
40
37
  expect((await vector.toDataVector())[1]?.pageId).toBe('home-page');
41
38
  });
39
+ it('Should Allow to match Signal Vector', async () => {
40
+ class MySignalA extends A_Signal<{ buttonId: string }> { }
41
+ class MySignalB extends A_Signal<{ pageId: string }> { }
42
+
43
+ const vector = new A_SignalVector<[MySignalA, MySignalB]>([
44
+ new MySignalA({ data: { buttonId: 'submit-order' } }),
45
+ new MySignalB({ data: { pageId: 'home-page' } })
46
+ ]);
47
+ const vector2 = new A_SignalVector<[MySignalA, MySignalB]>([
48
+ new MySignalA({ data: { buttonId: 'submit-order2' } }),
49
+ new MySignalB({ data: { pageId: 'home-page' } })
50
+ ]);
51
+ const vector3 = new A_SignalVector<[MySignalA, MySignalB]>([
52
+ new MySignalA({ data: { buttonId: 'submit-order' } }),
53
+ new MySignalB({ data: { pageId: 'home-page' } })
54
+ ]);
55
+
56
+ expect(vector.match(vector2)).toBe(false);
57
+ expect(vector.match(vector3)).toBe(true);
58
+ });
59
+ it('Should Allow to check if Signal Vector contains another Signal Vector', async () => {
60
+
61
+ class MySignalA extends A_Signal<{ buttonId: string }> { }
62
+ class MySignalB extends A_Signal<{ pageId: string }> { }
63
+ class MySignalC extends A_Signal<{ userId: string }> { }
64
+
65
+ const vector = new A_SignalVector<[MySignalA, MySignalB, MySignalC]>([
66
+ new MySignalA({ data: { buttonId: 'submit-order' } }),
67
+ new MySignalB({ data: { pageId: 'home-page' } }),
68
+ new MySignalC({ data: { userId: 'user123' } })
69
+ ]);
70
+ const vector2 = new A_SignalVector<[MySignalA, MySignalC]>([
71
+ new MySignalA({ data: { buttonId: 'submit-order' } }),
72
+ new MySignalC({ data: { userId: 'user123' } })
73
+ ]);
74
+ const vector3 = new A_SignalVector<[MySignalB]>([
75
+ new MySignalB({ data: { pageId: 'other-page' } })
76
+ ]);
77
+
78
+ expect(vector.contains(vector2)).toBe(true);
79
+ expect(vector.contains(vector3)).toBe(true);
80
+ expect(vector2.contains(vector)).toBe(false);
81
+
82
+ });
42
83
  it('Should Allow to get signals fro Signal Vector', async () => {
43
84
  class MySignalA extends A_Signal<{ buttonId: string }> { }
44
85
  class MySignalB extends A_Signal<{ pageId: string }> { }
45
86
  class MySignalC extends A_Signal<{ userId: string }> { }
46
87
 
47
- const vector = new A_SignalVector({
48
- structure: [MySignalA, MySignalB],
49
- values: [
50
- new MySignalA({ data: { buttonId: 'submit-order' } }),
51
- new MySignalB({ data: { pageId: 'home-page' } })
52
- ]
53
- });
88
+ const vector = new A_SignalVector([
89
+ new MySignalA({ data: { buttonId: 'submit-order' } }),
90
+ new MySignalB({ data: { pageId: 'home-page' } })
91
+ ]);
54
92
 
55
93
  const signalA = vector.get(MySignalA);
56
94
  const signalB = vector.get(MySignalB);