@adaas/a-utils 0.3.5 → 0.3.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/.conf/tsconfig.browser.json +4 -1
- package/.conf/tsconfig.node.json +5 -2
- package/dist/browser/a-service.mjs +12 -12
- package/dist/browser/a-service.mjs.map +1 -1
- package/dist/browser/a-signal.d.mts +51 -30
- package/dist/browser/a-signal.mjs +100 -65
- package/dist/browser/a-signal.mjs.map +1 -1
- package/dist/browser/chunk-S2RSPZXR.mjs +143 -0
- package/dist/browser/chunk-S2RSPZXR.mjs.map +1 -0
- package/dist/browser/helpers.d.mts +56 -0
- package/dist/browser/helpers.mjs +5 -0
- package/dist/browser/helpers.mjs.map +1 -0
- package/dist/node/{A-Signal.types-P5VKMKMs.d.mts → A-Signal.types-DxQHmPm6.d.mts} +23 -27
- package/dist/node/{A-Signal.types-P5VKMKMs.d.ts → A-Signal.types-DxQHmPm6.d.ts} +23 -27
- package/dist/node/helpers/A-Utils.helper.d.mts +56 -0
- package/dist/node/helpers/A-Utils.helper.d.ts +56 -0
- package/dist/node/helpers/A-Utils.helper.js +153 -0
- package/dist/node/helpers/A-Utils.helper.js.map +1 -0
- package/dist/node/helpers/A-Utils.helper.mjs +143 -0
- package/dist/node/helpers/A-Utils.helper.mjs.map +1 -0
- package/dist/node/helpers/index.d.mts +3 -0
- package/dist/node/helpers/index.d.ts +3 -0
- package/dist/node/helpers/index.js +12 -0
- package/dist/node/helpers/index.js.map +1 -0
- package/dist/node/helpers/index.mjs +4 -0
- package/dist/node/helpers/index.mjs.map +1 -0
- package/dist/node/lib/A-Service/A-Service.container.js +12 -12
- package/dist/node/lib/A-Service/A-Service.container.js.map +1 -1
- package/dist/node/lib/A-Service/A-Service.container.mjs +12 -12
- package/dist/node/lib/A-Service/A-Service.container.mjs.map +1 -1
- package/dist/node/lib/A-Signal/A-Signal.types.d.mts +1 -1
- package/dist/node/lib/A-Signal/A-Signal.types.d.ts +1 -1
- package/dist/node/lib/A-Signal/components/A-SignalBus.component.d.mts +1 -1
- package/dist/node/lib/A-Signal/components/A-SignalBus.component.d.ts +1 -1
- package/dist/node/lib/A-Signal/context/A-SignalConfig.context.d.mts +1 -1
- package/dist/node/lib/A-Signal/context/A-SignalConfig.context.d.ts +1 -1
- package/dist/node/lib/A-Signal/context/A-SignalState.context.d.mts +1 -1
- package/dist/node/lib/A-Signal/context/A-SignalState.context.d.ts +1 -1
- package/dist/node/lib/A-Signal/entities/A-Signal.entity.d.mts +1 -1
- package/dist/node/lib/A-Signal/entities/A-Signal.entity.d.ts +1 -1
- package/dist/node/lib/A-Signal/entities/A-Signal.entity.js +35 -59
- package/dist/node/lib/A-Signal/entities/A-Signal.entity.js.map +1 -1
- package/dist/node/lib/A-Signal/entities/A-Signal.entity.mjs +35 -59
- package/dist/node/lib/A-Signal/entities/A-Signal.entity.mjs.map +1 -1
- package/dist/node/lib/A-Signal/entities/A-SignalVector.entity.d.mts +29 -4
- package/dist/node/lib/A-Signal/entities/A-SignalVector.entity.d.ts +29 -4
- package/dist/node/lib/A-Signal/entities/A-SignalVector.entity.js +64 -5
- package/dist/node/lib/A-Signal/entities/A-SignalVector.entity.js.map +1 -1
- package/dist/node/lib/A-Signal/entities/A-SignalVector.entity.mjs +65 -6
- package/dist/node/lib/A-Signal/entities/A-SignalVector.entity.mjs.map +1 -1
- package/dist/node/lib/A-Signal/index.d.mts +1 -1
- package/dist/node/lib/A-Signal/index.d.ts +1 -1
- package/jest.config.ts +3 -1
- package/package.json +19 -3
- package/src/helpers/A-Utils.helper.ts +186 -0
- package/src/helpers/index.ts +1 -0
- package/src/lib/A-Service/A-Service.container.ts +12 -12
- package/src/lib/A-Signal/A-Signal.types.ts +2 -2
- package/src/lib/A-Signal/entities/A-Signal.entity.ts +44 -84
- package/src/lib/A-Signal/entities/A-SignalVector.entity.ts +80 -11
- package/tests/A-Signal.test.ts +16 -12
- package/tsconfig.json +4 -1
- package/tsup.config.ts +2 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { A_Caller, A_Component, A_Feature, A_Inject } from "@adaas/a-concept";
|
|
2
|
+
import { A_Frame } from "@adaas/a-frame";
|
|
3
|
+
import { A_ExecutionContext } from "@adaas/a-utils/a-execution";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@A_Frame.Component({
|
|
8
|
+
namespace: 'A-Utils',
|
|
9
|
+
name: 'A-UtilsHelper',
|
|
10
|
+
description: 'Utility helper class providing common functions for A-Utils library, such as hashing and serialization.'
|
|
11
|
+
})
|
|
12
|
+
export class A_UtilsHelper extends A_Component {
|
|
13
|
+
|
|
14
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
15
|
+
// ── Hashing ──────────────────────────────────────────────────────────────────
|
|
16
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Produces a deterministic, collision-resistant hash string for any JS value.
|
|
20
|
+
*
|
|
21
|
+
* Improvements over the legacy `createHash`:
|
|
22
|
+
* - **Null-safe** — handles `null` without throwing
|
|
23
|
+
* - **Function-aware serialization** — functions inside objects / arrays are
|
|
24
|
+
* serialized via `.toString()` so `{ fn: () => 1 }` ≠ `{}`
|
|
25
|
+
* - **FNV-1a 52-bit** — better avalanche / distribution than DJB2-32,
|
|
26
|
+
* and uses the safe JS integer range so the result is always positive
|
|
27
|
+
* - **Hex output** — compact, URL-safe, fixed-width (13 chars)
|
|
28
|
+
*
|
|
29
|
+
* @param value Any value: string, number, boolean, null, undefined,
|
|
30
|
+
* object, array, Map, Set, function, or a mix of these.
|
|
31
|
+
* @returns A 13-character lower-hex string (52-bit FNV-1a).
|
|
32
|
+
*/
|
|
33
|
+
static hash(value?: any): string {
|
|
34
|
+
const source = A_UtilsHelper.serialize(value);
|
|
35
|
+
return A_UtilsHelper.fnv1a52(source);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
40
|
+
// ── Serialization ────────────────────────────────────────────────────────────
|
|
41
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Converts any JS value into a deterministic string representation
|
|
45
|
+
* suitable for hashing.
|
|
46
|
+
*
|
|
47
|
+
* Key properties:
|
|
48
|
+
* - **Deterministic**: same logical value → same string every time
|
|
49
|
+
* - **Injective-ish**: structurally different values produce different
|
|
50
|
+
* strings (type tags prevent `"3"` vs `3` collisions)
|
|
51
|
+
* - **Recursive**: handles nested objects, arrays, Maps, Sets
|
|
52
|
+
* - **Function-aware**: serializes functions via `.toString()`
|
|
53
|
+
*
|
|
54
|
+
* @param value Anything.
|
|
55
|
+
* @returns A deterministic string.
|
|
56
|
+
*/
|
|
57
|
+
static serialize(value: any): string {
|
|
58
|
+
// Primitives & null/undefined
|
|
59
|
+
if (value === null) return '<null>';
|
|
60
|
+
if (value === undefined) return '<undefined>';
|
|
61
|
+
|
|
62
|
+
switch (typeof value) {
|
|
63
|
+
case 'string':
|
|
64
|
+
return `s:${value}`;
|
|
65
|
+
case 'number':
|
|
66
|
+
return `n:${value}`;
|
|
67
|
+
case 'boolean':
|
|
68
|
+
return `b:${value}`;
|
|
69
|
+
case 'bigint':
|
|
70
|
+
return `bi:${value}`;
|
|
71
|
+
case 'symbol':
|
|
72
|
+
return `sym:${value.toString()}`;
|
|
73
|
+
case 'function':
|
|
74
|
+
return `fn:${value.toString()}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Map
|
|
78
|
+
if (value instanceof Map) {
|
|
79
|
+
const entries = Array.from(value.entries())
|
|
80
|
+
.map(([k, v]) => `${A_UtilsHelper.serialize(k)}=>${A_UtilsHelper.serialize(v)}`)
|
|
81
|
+
.sort()
|
|
82
|
+
.join(',');
|
|
83
|
+
return `Map{${entries}}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Set
|
|
87
|
+
if (value instanceof Set) {
|
|
88
|
+
const items = Array.from(value.values())
|
|
89
|
+
.map(v => A_UtilsHelper.serialize(v))
|
|
90
|
+
.sort()
|
|
91
|
+
.join(',');
|
|
92
|
+
return `Set{${items}}`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Date
|
|
96
|
+
if (value instanceof Date) {
|
|
97
|
+
return `Date:${value.toISOString()}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// RegExp
|
|
101
|
+
if (value instanceof RegExp) {
|
|
102
|
+
return `RegExp:${value.toString()}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Array
|
|
106
|
+
if (Array.isArray(value)) {
|
|
107
|
+
const items = value.map(v => A_UtilsHelper.serialize(v)).join(',');
|
|
108
|
+
return `[${items}]`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// toJSON support (e.g. custom entities, ASEID, etc.)
|
|
112
|
+
if (typeof value.toJSON === 'function') {
|
|
113
|
+
return `json:${A_UtilsHelper.serialize(value.toJSON())}`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Plain object — sort keys for determinism, serialize functions too
|
|
117
|
+
const keys = Object.keys(value).sort();
|
|
118
|
+
const pairs = keys.map(k => `${k}:${A_UtilsHelper.serialize(value[k])}`).join(',');
|
|
119
|
+
return `{${pairs}}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
124
|
+
// ── FNV-1a (pure Number, no BigInt) ──────────────────────────────────────────
|
|
125
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* FNV-1a hash using two 32-bit halves to simulate a 52-bit space,
|
|
129
|
+
* without requiring BigInt.
|
|
130
|
+
*
|
|
131
|
+
* Works identically in:
|
|
132
|
+
* - All browsers (including Safari 13, IE11 polyfill targets, React Native)
|
|
133
|
+
* - Node.js (any version)
|
|
134
|
+
* - Web Workers, Service Workers, Deno, Bun
|
|
135
|
+
*
|
|
136
|
+
* - Better avalanche than DJB2 (each input bit affects many output bits)
|
|
137
|
+
* - ~52-bit effective space — vastly fewer collisions than 32-bit
|
|
138
|
+
* - Always produces a **positive** hex string of 13 characters
|
|
139
|
+
*
|
|
140
|
+
* @param input Pre-serialized string.
|
|
141
|
+
* @returns 13-character lower-hex string.
|
|
142
|
+
*/
|
|
143
|
+
private static fnv1a52(input: string): string {
|
|
144
|
+
// FNV-1a offset basis split into two 32-bit halves
|
|
145
|
+
let h1 = 0x811c9dc5; // low 32 bits
|
|
146
|
+
let h2 = 0x00000842; // high 20 bits (keeps us in 52-bit range)
|
|
147
|
+
|
|
148
|
+
// FNV prime = 0x01000193
|
|
149
|
+
const PRIME = 0x01000193;
|
|
150
|
+
|
|
151
|
+
for (let i = 0; i < input.length; i++) {
|
|
152
|
+
h1 ^= input.charCodeAt(i);
|
|
153
|
+
|
|
154
|
+
// Multiply h1 by prime (32-bit, unsigned)
|
|
155
|
+
const product = Math.imul(h1, PRIME);
|
|
156
|
+
h1 = product >>> 0;
|
|
157
|
+
|
|
158
|
+
// Carry overflow into h2, keep h2 within 20 bits
|
|
159
|
+
h2 = ((Math.imul(h2, PRIME) + (product / 0x100000000 >>> 0)) & 0xFFFFF) >>> 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Combine: h2 (20 bits) << 32 | h1 (32 bits) → 52-bit number
|
|
163
|
+
// Since 2^52 fits in Number.MAX_SAFE_INTEGER, this is exact.
|
|
164
|
+
const combined = h2 * 0x100000000 + h1;
|
|
165
|
+
|
|
166
|
+
return combined.toString(16).padStart(13, '0');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
// ==============================================================================
|
|
172
|
+
// =============== Instance methods for used for Injection ======================
|
|
173
|
+
// ==============================================================================
|
|
174
|
+
@A_Frame.Method({
|
|
175
|
+
description: 'Instance method wrapper for the static hash function, allowing it to be injected as a dependency.'
|
|
176
|
+
})
|
|
177
|
+
hash(
|
|
178
|
+
@A_Inject(A_Caller) caller: any,
|
|
179
|
+
@A_Inject(A_ExecutionContext) context: A_ExecutionContext,
|
|
180
|
+
@A_Inject(A_Feature) feature: A_Feature
|
|
181
|
+
) {
|
|
182
|
+
const hash = A_UtilsHelper.hash(caller);
|
|
183
|
+
|
|
184
|
+
context.set(feature.name, hash);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { A_UtilsHelper } from './A-Utils.helper';
|
|
@@ -26,11 +26,11 @@ export class A_Service extends A_Container {
|
|
|
26
26
|
*/
|
|
27
27
|
async load() {
|
|
28
28
|
try {
|
|
29
|
-
await this.call(A_ServiceFeatures.onBeforeLoad);
|
|
29
|
+
await this.call(A_ServiceFeatures.onBeforeLoad, this.scope);
|
|
30
30
|
|
|
31
|
-
await this.call(A_ServiceFeatures.onLoad);
|
|
31
|
+
await this.call(A_ServiceFeatures.onLoad, this.scope);
|
|
32
32
|
|
|
33
|
-
await this.call(A_ServiceFeatures.onAfterLoad);
|
|
33
|
+
await this.call(A_ServiceFeatures.onAfterLoad, this.scope);
|
|
34
34
|
|
|
35
35
|
} catch (error) {
|
|
36
36
|
|
|
@@ -56,7 +56,7 @@ export class A_Service extends A_Container {
|
|
|
56
56
|
|
|
57
57
|
this.scope.register(wrappedError);
|
|
58
58
|
|
|
59
|
-
await this.call(A_ServiceFeatures.onError);
|
|
59
|
+
await this.call(A_ServiceFeatures.onError, this.scope);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
}
|
|
@@ -68,11 +68,11 @@ export class A_Service extends A_Container {
|
|
|
68
68
|
*/
|
|
69
69
|
async start() {
|
|
70
70
|
try {
|
|
71
|
-
await this.call(A_ServiceFeatures.onBeforeStart);
|
|
71
|
+
await this.call(A_ServiceFeatures.onBeforeStart, this.scope);
|
|
72
72
|
|
|
73
|
-
await this.call(A_ServiceFeatures.onStart);
|
|
73
|
+
await this.call(A_ServiceFeatures.onStart, this.scope);
|
|
74
74
|
|
|
75
|
-
await this.call(A_ServiceFeatures.onAfterStart);
|
|
75
|
+
await this.call(A_ServiceFeatures.onAfterStart, this.scope);
|
|
76
76
|
|
|
77
77
|
} catch (error) {
|
|
78
78
|
|
|
@@ -98,7 +98,7 @@ export class A_Service extends A_Container {
|
|
|
98
98
|
|
|
99
99
|
this.scope.register(wrappedError);
|
|
100
100
|
|
|
101
|
-
await this.call(A_ServiceFeatures.onError);
|
|
101
|
+
await this.call(A_ServiceFeatures.onError, this.scope);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
}
|
|
@@ -109,11 +109,11 @@ export class A_Service extends A_Container {
|
|
|
109
109
|
*/
|
|
110
110
|
async stop() {
|
|
111
111
|
try {
|
|
112
|
-
await this.call(A_ServiceFeatures.onBeforeStop);
|
|
112
|
+
await this.call(A_ServiceFeatures.onBeforeStop, this.scope);
|
|
113
113
|
|
|
114
|
-
await this.call(A_ServiceFeatures.onStop);
|
|
114
|
+
await this.call(A_ServiceFeatures.onStop, this.scope);
|
|
115
115
|
|
|
116
|
-
await this.call(A_ServiceFeatures.onAfterStop);
|
|
116
|
+
await this.call(A_ServiceFeatures.onAfterStop, this.scope);
|
|
117
117
|
|
|
118
118
|
} catch (error) {
|
|
119
119
|
|
|
@@ -139,7 +139,7 @@ export class A_Service extends A_Container {
|
|
|
139
139
|
|
|
140
140
|
this.scope.register(wrappedError);
|
|
141
141
|
|
|
142
|
-
await this.call(A_ServiceFeatures.onError);
|
|
142
|
+
await this.call(A_ServiceFeatures.onError, this.scope);
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
@@ -51,7 +51,7 @@ export type A_SignalVector_Serialized = A_TYPES__Entity_Serialized & {
|
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
|
|
54
|
-
export type A_Signal_Init<T extends
|
|
54
|
+
export type A_Signal_Init<T extends any = any> = {
|
|
55
55
|
/**
|
|
56
56
|
* Possible signal id
|
|
57
57
|
*
|
|
@@ -68,7 +68,7 @@ export type A_Signal_Init<T extends Record<string, any> = Record<string, any>> =
|
|
|
68
68
|
data: T
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
export type A_Signal_Serialized<T extends
|
|
71
|
+
export type A_Signal_Serialized<T extends any = any> = {
|
|
72
72
|
/**
|
|
73
73
|
* The signal data
|
|
74
74
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { A_Entity
|
|
1
|
+
import { A_Entity } from "@adaas/a-concept";
|
|
2
2
|
import { A_Signal_Init, A_Signal_Serialized } from "../A-Signal.types";
|
|
3
3
|
import { A_Frame } from "@adaas/a-frame";
|
|
4
|
+
import { A_UtilsHelper } from "@adaas/a-utils/helpers";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* A Signal Entity is an individual signal instance that carries data.
|
|
@@ -21,23 +22,14 @@ import { A_Frame } from "@adaas/a-frame";
|
|
|
21
22
|
description: 'A Signal Entity represents an individual signal instance that carries data, used for managing state within an application context. Signals are designed to reflect the current state rather than individual events, making them suitable for scenarios where state monitoring and real-time updates are essential.'
|
|
22
23
|
})
|
|
23
24
|
export class A_Signal<
|
|
24
|
-
_TSignalDataType extends
|
|
25
|
-
|
|
25
|
+
_TSignalDataType extends any = any,
|
|
26
|
+
_TSignalSerializedDataType extends any = _TSignalDataType,
|
|
27
|
+
> extends A_Entity<A_Signal_Init<_TSignalDataType>, A_Signal_Serialized<_TSignalSerializedDataType>> {
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
// ========================================================================
|
|
29
31
|
// ========================== Static Methods ==============================
|
|
30
32
|
// ========================================================================
|
|
31
|
-
/**
|
|
32
|
-
* Allows to define default data for the signal.
|
|
33
|
-
*
|
|
34
|
-
* If no data is provided during initialization, the default data will be used.
|
|
35
|
-
*
|
|
36
|
-
* @returns
|
|
37
|
-
*/
|
|
38
|
-
static async default(): Promise<A_Signal | undefined> {
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
33
|
|
|
42
34
|
// ========================================================================
|
|
43
35
|
// ========================== Instance Properties ========================
|
|
@@ -48,69 +40,6 @@ export class A_Signal<
|
|
|
48
40
|
*/
|
|
49
41
|
data!: _TSignalDataType;
|
|
50
42
|
|
|
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
43
|
/**
|
|
115
44
|
* This method compares the current signal with another signal instance by deduplication ID
|
|
116
45
|
* this id can be configured during initialization with the "id" property.
|
|
@@ -131,15 +60,37 @@ export class A_Signal<
|
|
|
131
60
|
|
|
132
61
|
return true;
|
|
133
62
|
}
|
|
134
|
-
|
|
135
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Allows to define default data for the signal.
|
|
66
|
+
*
|
|
67
|
+
* If no data is provided during initialization, the default data will be used.
|
|
68
|
+
*
|
|
69
|
+
* @returns
|
|
70
|
+
*/
|
|
71
|
+
fromUndefined(): void {
|
|
72
|
+
const name = (this.constructor as typeof A_Entity).entity;
|
|
73
|
+
|
|
74
|
+
this.data = undefined as unknown as _TSignalDataType;
|
|
136
75
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
76
|
+
const identity = {
|
|
77
|
+
name: name,
|
|
78
|
+
data: this.data
|
|
79
|
+
};
|
|
141
80
|
|
|
81
|
+
const id = A_UtilsHelper.hash(identity);
|
|
142
82
|
|
|
83
|
+
this.aseid = this.generateASEID({
|
|
84
|
+
entity: name,
|
|
85
|
+
id: id,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Allows to initialize the signal from a new signal entity. This is useful for example when we want to create a new instance of the signal entity with the same data as another instance, but with a different ASEID.
|
|
91
|
+
*
|
|
92
|
+
* @param newEntity
|
|
93
|
+
*/
|
|
143
94
|
fromNew(newEntity: A_Signal_Init<_TSignalDataType>): void {
|
|
144
95
|
this.data = newEntity.data;
|
|
145
96
|
|
|
@@ -148,7 +99,7 @@ export class A_Signal<
|
|
|
148
99
|
data: this.data
|
|
149
100
|
};
|
|
150
101
|
|
|
151
|
-
const id =
|
|
102
|
+
const id = A_UtilsHelper.hash(identity);
|
|
152
103
|
|
|
153
104
|
this.aseid = this.generateASEID({
|
|
154
105
|
entity: newEntity.name,
|
|
@@ -156,11 +107,20 @@ export class A_Signal<
|
|
|
156
107
|
});
|
|
157
108
|
}
|
|
158
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Allows to initialize the signal from a serialized version of the signal. This is useful for example when we receive a signal from the server and we want to create an instance of the signal entity from the received data.
|
|
112
|
+
*
|
|
113
|
+
* @param serializedEntity
|
|
114
|
+
*/
|
|
115
|
+
fromJSON(serializedEntity: A_Signal_Serialized<_TSignalSerializedDataType>): void {
|
|
116
|
+
super.fromJSON(serializedEntity);
|
|
117
|
+
this.data = serializedEntity.data as unknown as _TSignalDataType;
|
|
118
|
+
}
|
|
159
119
|
|
|
160
|
-
toJSON(): A_Signal_Serialized<
|
|
120
|
+
toJSON(): A_Signal_Serialized<_TSignalSerializedDataType> {
|
|
161
121
|
return {
|
|
162
122
|
...super.toJSON(),
|
|
163
|
-
data: this.data
|
|
123
|
+
data: this.data as unknown as _TSignalSerializedDataType
|
|
164
124
|
};
|
|
165
125
|
}
|
|
166
126
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A_Entity, A_Scope, A_TYPES__Component_Constructor, A_TYPES__Entity_Constructor } from "@adaas/a-concept";
|
|
1
|
+
import { A_Entity, A_Scope, A_TypeGuards, A_TYPES__Component_Constructor, A_TYPES__Entity_Constructor } from "@adaas/a-concept";
|
|
2
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";
|
|
@@ -107,10 +107,60 @@ export class A_SignalVector<
|
|
|
107
107
|
};
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Checks that 2 vectors are identical by types and data
|
|
112
|
+
*
|
|
113
|
+
* e.g. [UserSignInSignal, UserStatusSignal] is equal to [UserSignInSignal, UserStatusSignal] with the same data,
|
|
114
|
+
* but not equal to [UserStatusSignal, UserSignInSignal] or [UserSignInSignal, UserStatusSignal] with different data.
|
|
115
|
+
*
|
|
116
|
+
* @param other
|
|
117
|
+
* @returns
|
|
118
|
+
*/
|
|
119
|
+
equals(other: A_SignalVector<TSignals>): boolean {
|
|
120
|
+
if (this.structure.length !== other.structure.length) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
for (let i = 0; i < this.structure.length; i++) {
|
|
125
|
+
const thisSignalConstructor = this.structure[i];
|
|
126
|
+
const otherSignalConstructor = other.structure[i];
|
|
127
|
+
|
|
128
|
+
if (thisSignalConstructor !== otherSignalConstructor) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const thisSignalIndex = this._signals.findIndex(s => s.constructor === thisSignalConstructor);
|
|
133
|
+
const otherSignalIndex = other._signals.findIndex(s => s.constructor === otherSignalConstructor);
|
|
134
|
+
|
|
135
|
+
if (thisSignalIndex !== otherSignalIndex) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const thisSignal = thisSignalIndex !== -1 ? this._signals[thisSignalIndex] : undefined;
|
|
140
|
+
const otherSignal = otherSignalIndex !== -1 ? other._signals[otherSignalIndex] : undefined;
|
|
141
|
+
|
|
142
|
+
if (thisSignal && otherSignal) {
|
|
143
|
+
if (!thisSignal.compare(otherSignal)) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
} else if (thisSignal || otherSignal) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
|
|
110
154
|
|
|
111
155
|
/**
|
|
112
156
|
* 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
|
|
157
|
+
* This method returns true if all signals in the vector A match the corresponding signals in vector B, and false otherwise.
|
|
158
|
+
*
|
|
159
|
+
*
|
|
160
|
+
* e.g. [UserSignInSignal, UserStatusSignal] matches [UserStatusSignal, UserSignInSignal] with the same data,
|
|
161
|
+
*
|
|
162
|
+
* but not matches [UserSignInSignal, UserStatusSignal] with different data or [UserSignInSignal] or [UserSignInSignal, UserStatusSignal, UserActivitySignal].
|
|
163
|
+
*
|
|
114
164
|
*
|
|
115
165
|
* @param other
|
|
116
166
|
* @returns
|
|
@@ -146,13 +196,32 @@ export class A_SignalVector<
|
|
|
146
196
|
return true;
|
|
147
197
|
}
|
|
148
198
|
|
|
149
|
-
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Checks if the current Signal Vector includes all signals from another Signal Vector, regardless of order.
|
|
202
|
+
*
|
|
203
|
+
* e.g. [UserSignInSignal, UserStatusSignal] includes [UserStatusSignal] with the same data,
|
|
204
|
+
* but not includes [UserStatusSignal] with different data or [UserActivitySignal].
|
|
205
|
+
*
|
|
206
|
+
* @param other
|
|
207
|
+
*/
|
|
208
|
+
includes(other: A_SignalVector<TSignals>): boolean {
|
|
209
|
+
for (const signalConstructor of other.structure) {
|
|
210
|
+
const signalIndex = this._signals.findIndex(s => s.constructor === signalConstructor);
|
|
211
|
+
if (signalIndex === -1) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
150
219
|
/**
|
|
151
220
|
* This method should ensure that the current Signal Vector contains all signals from the provided Signal Vector.
|
|
152
221
|
*
|
|
153
222
|
* @param signal
|
|
154
223
|
*/
|
|
155
|
-
contains(signal: A_SignalVector): boolean{
|
|
224
|
+
contains(signal: A_SignalVector): boolean {
|
|
156
225
|
for (const signalConstructor of signal.structure) {
|
|
157
226
|
const signalIndex = this._signals.findIndex(s => s.constructor === signalConstructor);
|
|
158
227
|
if (signalIndex === -1) {
|
|
@@ -171,12 +240,12 @@ export class A_SignalVector<
|
|
|
171
240
|
has(signalConstructor: A_TYPES__Entity_Constructor<A_Signal>): boolean
|
|
172
241
|
has(param1: A_Signal | A_TYPES__Entity_Constructor<A_Signal>): boolean {
|
|
173
242
|
let signalConstructor: A_TYPES__Entity_Constructor<A_Signal>;
|
|
174
|
-
if (param1
|
|
243
|
+
if (A_TypeGuards.isEntityInstance(param1)) {
|
|
175
244
|
signalConstructor = param1.constructor as A_TYPES__Entity_Constructor<A_Signal>;
|
|
176
245
|
} else {
|
|
177
246
|
signalConstructor = param1;
|
|
178
247
|
}
|
|
179
|
-
return this.structure.includes(signalConstructor
|
|
248
|
+
return this.structure.includes(signalConstructor);
|
|
180
249
|
}
|
|
181
250
|
|
|
182
251
|
/**
|
|
@@ -210,11 +279,11 @@ export class A_SignalVector<
|
|
|
210
279
|
* @param structure - Optional structure to override the default ordering
|
|
211
280
|
* @returns Array of signal instances in the specified order
|
|
212
281
|
*/
|
|
213
|
-
|
|
282
|
+
toVector<
|
|
214
283
|
T extends Array<A_Signal> = TSignals,
|
|
215
284
|
>(
|
|
216
285
|
structure?: A_Signal_TSignalsConstructors<T>
|
|
217
|
-
):
|
|
286
|
+
): T {
|
|
218
287
|
const usedStructure = structure || this.structure;
|
|
219
288
|
|
|
220
289
|
return usedStructure.map((signalConstructor) => {
|
|
@@ -231,11 +300,11 @@ export class A_SignalVector<
|
|
|
231
300
|
* @param structure - Optional structure to override the default ordering
|
|
232
301
|
* @returns Array of serialized signal data in the specified order
|
|
233
302
|
*/
|
|
234
|
-
|
|
303
|
+
toDataVector<
|
|
235
304
|
T extends A_Signal[] = TSignals,
|
|
236
305
|
>(
|
|
237
306
|
structure?: A_Signal_TSignalsConstructors<T>
|
|
238
|
-
):
|
|
307
|
+
): A_SignalTValueArray<T> {
|
|
239
308
|
|
|
240
309
|
const usedStructure = structure || this.structure;
|
|
241
310
|
|
|
@@ -246,7 +315,7 @@ export class A_SignalVector<
|
|
|
246
315
|
let data: any;
|
|
247
316
|
if (signalIndex === -1) {
|
|
248
317
|
|
|
249
|
-
data =
|
|
318
|
+
data = new (signalConstructor as typeof A_Signal)()
|
|
250
319
|
|
|
251
320
|
} else {
|
|
252
321
|
const signal = this._signals[signalIndex];
|
package/tests/A-Signal.test.ts
CHANGED
|
@@ -29,8 +29,8 @@ describe('A-Signal tests', () => {
|
|
|
29
29
|
expect(vector).toBeDefined();
|
|
30
30
|
expect(vector).toBeInstanceOf(A_SignalVector);
|
|
31
31
|
expect(vector.length).toBe(2);
|
|
32
|
-
expect((
|
|
33
|
-
expect((
|
|
32
|
+
expect((vector.toDataVector())[0]?.buttonId).toBe('submit-order');
|
|
33
|
+
expect((vector.toDataVector())[1]?.pageId).toBe('home-page');
|
|
34
34
|
});
|
|
35
35
|
it('Should Allow to match Signal Vector', async () => {
|
|
36
36
|
class MySignalA extends A_Signal<{ buttonId: string }> { }
|
|
@@ -57,7 +57,7 @@ describe('A-Signal tests', () => {
|
|
|
57
57
|
class MySignalA extends A_Signal<{ buttonId: string }> { }
|
|
58
58
|
class MySignalB extends A_Signal<{ pageId: string }> { }
|
|
59
59
|
class MySignalC extends A_Signal<{ userId: string }> { }
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
const vector = new A_SignalVector<[MySignalA, MySignalB, MySignalC]>([
|
|
62
62
|
new MySignalA({ data: { buttonId: 'submit-order' } }),
|
|
63
63
|
new MySignalB({ data: { pageId: 'home-page' } }),
|
|
@@ -142,7 +142,7 @@ describe('A-Signal tests', () => {
|
|
|
142
142
|
expect(result).toBeDefined();
|
|
143
143
|
expect(result).toBeInstanceOf(A_SignalVector);
|
|
144
144
|
expect(result!.length).toBe(1);
|
|
145
|
-
expect((
|
|
145
|
+
expect((result!.toDataVector())[0]?.buttonId).toBe('submit-order');
|
|
146
146
|
|
|
147
147
|
});
|
|
148
148
|
it('Should Allow to work with custom Signals', async () => {
|
|
@@ -150,10 +150,14 @@ describe('A-Signal tests', () => {
|
|
|
150
150
|
class UserIntentionSignal extends A_Signal<{ buttonId: string }> { }
|
|
151
151
|
|
|
152
152
|
class UserMousePositionSignal extends A_Signal<{ x: number, y: number }> {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
153
|
+
fromUndefined(): void {
|
|
154
|
+
super.fromNew({
|
|
155
|
+
name: 'UserMousePositionSignal',
|
|
156
|
+
data: {
|
|
157
|
+
x: 0,
|
|
158
|
+
y: 0
|
|
159
|
+
}
|
|
160
|
+
})
|
|
157
161
|
}
|
|
158
162
|
}
|
|
159
163
|
class UserElementHoverSignal extends A_Signal<{ elementId: string }> { }
|
|
@@ -208,10 +212,10 @@ describe('A-Signal tests', () => {
|
|
|
208
212
|
expect(result).toBeInstanceOf(A_SignalVector);
|
|
209
213
|
expect(result!.length).toBe(3);
|
|
210
214
|
|
|
211
|
-
expect((
|
|
212
|
-
expect((
|
|
213
|
-
expect((
|
|
214
|
-
expect((
|
|
215
|
+
expect((result!.toDataVector())[0]?.buttonId).toBe('submit-order');
|
|
216
|
+
expect((result!.toDataVector())[1]?.x).toBe(0);
|
|
217
|
+
expect((result!.toDataVector())[1]?.y).toBe(0);
|
|
218
|
+
expect((result!.toDataVector())[2]).toBeUndefined();
|
|
215
219
|
|
|
216
220
|
});
|
|
217
221
|
|