@adaas/a-concept 0.3.3 → 0.3.5

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-concept",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "A-Concept is a framework of the new generation that is tailored to use AI, enabling developers to create AI-powered applications with ease. It provides a structured approach to building, managing, and deploying AI-driven solutions.",
5
5
  "license": "Apache-2.0",
6
6
  "author": {
@@ -65,7 +65,7 @@ export function A_Abstraction_Extend(
65
65
  }
66
66
 
67
67
  let metaKey;
68
- const meta: A_ContainerMeta | A_ComponentMeta = A_Context.meta(target);
68
+ const meta: A_Meta = A_Context.meta(target);
69
69
 
70
70
  switch (true) {
71
71
  case A_TypeGuards.isContainerConstructor(target) || A_TypeGuards.isContainerInstance(target):
@@ -50,7 +50,7 @@ export class A_ComponentMeta<T extends A_TYPES__ComponentMeta = A_TYPES__Compone
50
50
  before: extension.before || '',
51
51
  after: extension.after || '',
52
52
  throwOnError: extension.throwOnError || true,
53
- override: ''
53
+ override: extension.override || '',
54
54
  });
55
55
 
56
56
  });
@@ -13,7 +13,7 @@ import { A_TYPES__FeatureDefineDecoratorMeta } from "@adaas/a-concept/a-feature"
13
13
 
14
14
 
15
15
 
16
- export class A_ContainerMeta extends A_Meta<A_TYPES__ContainerMeta> {
16
+ export class A_ContainerMeta<T extends A_TYPES__ContainerMeta = A_TYPES__ContainerMeta> extends A_Meta<T> {
17
17
 
18
18
 
19
19
 
@@ -8,6 +8,7 @@ import {
8
8
  } from "@adaas/a-concept/a-scope";
9
9
  import {
10
10
  A_Meta,
11
+ A_TYPES__Meta_Constructor,
11
12
  A_TYPES__MetaLinkedComponentConstructors,
12
13
  A_TYPES__MetaLinkedComponents
13
14
  } from "@adaas/a-concept/a-meta";
@@ -376,7 +377,7 @@ export class A_Context {
376
377
  */
377
378
  component: A_TYPES__Component_Constructor,
378
379
  ): T
379
- static meta<T extends A_ComponentMeta, S extends A_Component>(
380
+ static meta<T extends A_ComponentMeta<any>, S extends A_Component>(
380
381
  /**
381
382
  * Get meta for the specific component instance.
382
383
  */
@@ -442,7 +443,7 @@ export class A_Context {
442
443
  ) throw new A_ContextError(A_ContextError.InvalidMetaParameterError, `Invalid parameter provided to get meta. Component of type ${componentName} is not allowed for meta storage. Only A_Container, A_Component and A_Entity are allowed.`);
443
444
 
444
445
  let property: A_TYPES__MetaLinkedComponentConstructors;
445
- let metaType: typeof A_Meta<T> | typeof A_ContainerMeta | typeof A_ComponentMeta | typeof A_EntityMeta
446
+ let metaType: A_TYPES__Meta_Constructor<A_Meta<any>>
446
447
 
447
448
  switch (true) {
448
449
  // 1) If param1 is instance of A_Container
@@ -833,6 +834,17 @@ export class A_Context {
833
834
  steps.delete(`${A_CommonHelper.getComponentName(inherited)}.${declaration.handler}`);
834
835
  }
835
836
 
837
+ // Check if the declaration has an override regexp and remove matching steps
838
+ // Match against both the full step key (Component.handler) and the handler alone
839
+ if (declaration.override) {
840
+ const overrideRegexp = new RegExp(declaration.override);
841
+ for (const [stepKey, step] of steps) {
842
+ if (overrideRegexp.test(stepKey) || overrideRegexp.test(step.handler)) {
843
+ steps.delete(stepKey);
844
+ }
845
+ }
846
+ }
847
+
836
848
  steps.set(`${A_CommonHelper.getComponentName(cmp)}.${declaration.handler}`, {
837
849
  dependency: new A_Dependency(cmp),
838
850
  ...declaration
@@ -5,14 +5,15 @@ import {
5
5
  A_TYPES__IEntity,
6
6
  } from "./A-Entity.types";
7
7
  import { A_Scope } from "@adaas/a-concept/a-scope";
8
- import { A_FormatterHelper} from "@adaas/a-concept/helpers/A_Formatter.helper";
9
- import { A_IdentityHelper} from "@adaas/a-concept/helpers/A_Identity.helper";
8
+ import { A_FormatterHelper } from "@adaas/a-concept/helpers/A_Formatter.helper";
9
+ import { A_IdentityHelper } from "@adaas/a-concept/helpers/A_Identity.helper";
10
10
  import {
11
11
  ASEID,
12
12
  A_TYPES__ASEID_Constructor
13
13
  } from "@adaas/a-concept/aseid";
14
14
  import { A_EntityError } from "./A-Entity.error";
15
15
  import { A_Feature } from "@adaas/a-concept/a-feature";
16
+ import { A_TYPES__EntityFeatures } from "./A-Entity.constants";
16
17
 
17
18
 
18
19
  /**
@@ -289,24 +290,24 @@ export class A_Entity<
289
290
  /**
290
291
  * The default method that can be called and extended to load entity data.
291
292
  */
292
- async load(
293
+ load(
293
294
  scope?: A_Scope,
294
- ) {
295
- return this.call('load', scope);
295
+ ): Promise<void> | void {
296
+ return this.call(A_TYPES__EntityFeatures.LOAD, scope);
296
297
  }
297
298
 
298
299
  /**
299
300
  * The default method that can be called and extended to destroy entity data.
300
301
  */
301
- async destroy(scope?: A_Scope) {
302
- return this.call('destroy', scope);
302
+ destroy(scope?: A_Scope): Promise<void> | void {
303
+ return this.call(A_TYPES__EntityFeatures.DESTROY, scope);
303
304
  }
304
305
 
305
306
  /**
306
307
  * The default method that can be called and extended to save entity data.
307
308
  */
308
- async save(scope?: A_Scope) {
309
- return this.call('save', scope);
309
+ save(scope?: A_Scope): Promise<void> | void {
310
+ return this.call(A_TYPES__EntityFeatures.SAVE, scope);
310
311
  }
311
312
 
312
313
 
@@ -5,8 +5,8 @@ export enum A_TYPES__EntityMetaKey {
5
5
  INJECTIONS = 'a-component-injections',
6
6
  }
7
7
 
8
- export enum A_TYPES__EntityFeatures {
9
- SAVE = 'save',
10
- DESTROY = 'destroy',
11
- LOAD = 'load'
12
- }
8
+ export const A_TYPES__EntityFeatures = {
9
+ SAVE: '_A_Entity__Save',
10
+ DESTROY: '_A_Entity__Destroy',
11
+ LOAD: '_A_Entity__Load'
12
+ } as const;
@@ -5,7 +5,7 @@ import { A_TYPES__FeatureDefineDecoratorMeta } from "@adaas/a-concept/a-feature"
5
5
  import { A_TYPES__A_InjectDecorator_Meta } from "@adaas/a-concept/a-inject";
6
6
 
7
7
 
8
- export class A_EntityMeta extends A_Meta<A_TYPES__EntityMeta> {
8
+ export class A_EntityMeta<T extends A_TYPES__EntityMeta = A_TYPES__EntityMeta> extends A_Meta<T> {
9
9
 
10
10
  /**
11
11
  * Returns all features defined in the Container
@@ -1,7 +1,7 @@
1
1
  import { A_Meta } from "@adaas/a-concept/a-meta";
2
2
  import { A_Entity } from "./A-Entity.class";
3
3
  import { ASEID } from "@adaas/a-concept/aseid";
4
- import { A_TYPES__EntityMetaKey } from "./A-Entity.constants";
4
+ import { A_TYPES__EntityFeatures, A_TYPES__EntityMetaKey } from "./A-Entity.constants";
5
5
  import { A_TYPES__FeatureDefineDecoratorMeta, A_TYPES__FeatureExtendDecoratorMeta } from "@adaas/a-concept/a-feature";
6
6
  import { A_TYPES__A_InjectDecorator_Meta } from "@adaas/a-concept/a-inject";
7
7
  import { A_TYPES__Ctor } from "@adaas/a-concept/types";
@@ -88,3 +88,4 @@ export type A_TYPES__EntityMeta = {
88
88
 
89
89
 
90
90
 
91
+ export type A_TYPES__EntityFeatureNames = typeof A_TYPES__EntityFeatures[keyof typeof A_TYPES__EntityFeatures];
@@ -843,7 +843,7 @@ export class A_Scope<
843
843
  /**
844
844
  * Provide the component constructor or its name to retrieve its constructor
845
845
  */
846
- component: A_TYPES__Ctor<T>
846
+ component: A_TYPES__Ctor<T>
847
847
  ): A_TYPES__Component_Constructor<T> | undefined
848
848
  resolveConstructor<T extends A_Entity>(
849
849
  /**
@@ -888,7 +888,7 @@ export class A_Scope<
888
888
  .find((e) => A_CommonHelper.isInheritedFrom(e, name)) as A_TYPES__Error_Constructor<T> | undefined;
889
889
  }
890
890
 
891
- if(!A_TypeGuards.isString(name))
891
+ if (!A_TypeGuards.isString(name))
892
892
  throw new A_ScopeError(
893
893
  A_ScopeError.ResolutionError,
894
894
  `Invalid constructor name provided: ${name}`
@@ -1865,7 +1865,12 @@ export class A_Scope<
1865
1865
  */
1866
1866
  entity: A_Entity
1867
1867
  ): void
1868
-
1868
+ deregister<T extends A_TYPES__A_DependencyInjectable>(
1869
+ /**
1870
+ * Provide an entity instance to register it in the scope
1871
+ */
1872
+ component: T
1873
+ ): void
1869
1874
  deregister(
1870
1875
  param1: unknown
1871
1876
  ): void {
@@ -16,6 +16,11 @@ export class A_StepsManager {
16
16
  public tempMark: Set<string>;
17
17
  public sortedEntities: string[];
18
18
 
19
+ /**
20
+ * Maps each step instance to a unique ID.
21
+ * Duplicate steps (same dependency.name + handler) get indexed suffixes (e.g., #1, #2).
22
+ */
23
+ private _uniqueIdMap: Map<A_TYPES__A_StageStep, string> = new Map();
19
24
 
20
25
  private _isBuilt: boolean = false;
21
26
 
@@ -27,6 +32,7 @@ export class A_StepsManager {
27
32
  this.tempMark = new Set();
28
33
  this.sortedEntities = [];
29
34
 
35
+ this.assignUniqueIds();
30
36
  }
31
37
 
32
38
  private prepareSteps(
@@ -44,20 +50,68 @@ export class A_StepsManager {
44
50
  }));
45
51
  }
46
52
 
47
- private ID(step: A_TYPES__A_StageStep) {
53
+ /**
54
+ * Returns the base (non-unique) ID for a step: `dependency.name.handler`
55
+ */
56
+ private baseID(step: A_TYPES__A_StageStep) {
48
57
  return `${step.dependency.name}.${step.handler}`;
49
58
  }
50
59
 
60
+ /**
61
+ * Returns the unique ID assigned to a specific step instance.
62
+ * Falls back to baseID if not yet assigned.
63
+ */
64
+ private ID(step: A_TYPES__A_StageStep) {
65
+ return this._uniqueIdMap.get(step) || this.baseID(step);
66
+ }
67
+
68
+ /**
69
+ * Assigns unique IDs to all steps.
70
+ * Duplicate base IDs get an index suffix (#0, #1, ...).
71
+ * Steps with unique base IDs keep their original ID (no suffix).
72
+ */
73
+ private assignUniqueIds() {
74
+ // Count occurrences of each base ID
75
+ const counts = new Map<string, number>();
76
+ for (const step of this.entities) {
77
+ const base = this.baseID(step);
78
+ counts.set(base, (counts.get(base) || 0) + 1);
79
+ }
80
+
81
+ // Assign unique IDs with index suffix only for duplicates
82
+ const indexTracker = new Map<string, number>();
83
+ for (const step of this.entities) {
84
+ const base = this.baseID(step);
85
+ if (counts.get(base)! > 1) {
86
+ const idx = indexTracker.get(base) || 0;
87
+ this._uniqueIdMap.set(step, `${base}#${idx}`);
88
+ indexTracker.set(base, idx + 1);
89
+ } else {
90
+ this._uniqueIdMap.set(step, base);
91
+ }
92
+ }
93
+ }
94
+
51
95
  private buildGraph() {
52
96
  if (this._isBuilt) return;
53
97
  this._isBuilt = true;
54
98
 
55
- // Filter override
99
+ // Filter override — match against both the full base ID and handler name
100
+ // so both formats work (e.g., `^.*\.step(4|5)$` and `^test1$`)
101
+ // A step's own override pattern should not cause it to filter itself out
56
102
  this.entities = this.entities
57
103
  .filter((step, i, self) =>
58
- !self.some(s => s.override ? new RegExp(s.override).test(this.ID(step)) : false)
104
+ !self.some((s, j) => {
105
+ if (i === j || !s.override) return false;
106
+ const re = new RegExp(s.override);
107
+ return re.test(this.baseID(step)) || re.test(step.handler);
108
+ })
59
109
  );
60
110
 
111
+ // Re-assign unique IDs after filtering
112
+ this._uniqueIdMap.clear();
113
+ this.assignUniqueIds();
114
+
61
115
  // Initialize graph nodes
62
116
  this.entities.forEach(entity => this.graph.set(this.ID(entity), new Set()));
63
117
 
@@ -90,12 +144,12 @@ export class A_StepsManager {
90
144
  });
91
145
  }
92
146
 
93
- // Match entities by name or regex
147
+ // Match entities by name or regex — matches against the base ID for pattern compatibility
94
148
  private matchEntities(entityId: string, pattern: string): string[] {
95
149
  const regex = new RegExp(pattern);
96
150
 
97
151
  return this.entities
98
- .filter(entity => regex.test(this.ID(entity)) && this.ID(entity) !== entityId)
152
+ .filter(entity => regex.test(this.baseID(entity)) && this.ID(entity) !== entityId)
99
153
  .map(entity => this.ID(entity));
100
154
  }
101
155
 
@@ -1038,4 +1038,80 @@ describe('A-Feature tests', () => {
1038
1038
  'ChildComponent_A.test'
1039
1039
  ]);
1040
1040
  })
1041
+ it('Should keep override definition', async () => {
1042
+
1043
+ const resultChain: string[] = [];
1044
+
1045
+ class ChildComponent_A extends A_Component {
1046
+ @A_Feature.Extend({
1047
+ name: 'testFeature',
1048
+ })
1049
+ async test1() {
1050
+ resultChain.push('ChildComponent_A.test1');
1051
+ }
1052
+
1053
+ @A_Feature.Extend({
1054
+ name: 'testFeature',
1055
+ override: ['test1']
1056
+ })
1057
+ async test2() {
1058
+ resultChain.push('ChildComponent_A.test2');
1059
+ }
1060
+ }
1061
+
1062
+ const testScope = new A_Scope({ name: 'TestScope', components: [ChildComponent_A] });
1063
+
1064
+ const featureDefinition = A_Context.featureTemplate('testFeature', testScope.resolve(ChildComponent_A)!, testScope);
1065
+
1066
+
1067
+ expect(featureDefinition).toBeDefined();
1068
+ expect(featureDefinition.length).toBe(1);
1069
+ expect(featureDefinition[0].handler).toBe('test2');
1070
+
1071
+ await testScope.resolve(ChildComponent_A)!.call('testFeature');
1072
+
1073
+ expect(resultChain).toEqual([
1074
+ 'ChildComponent_A.test2'
1075
+ ]);
1076
+ })
1077
+
1078
+ it('Should allow to run feature with the same steps', async () => {
1079
+
1080
+ const resultChain: string[] = [];
1081
+
1082
+ class ComponentA extends A_Component {
1083
+ @A_Feature.Extend({
1084
+ name: 'testFeature',
1085
+ })
1086
+ async feature1() {
1087
+ resultChain.push('ComponentA.feature1');
1088
+ }
1089
+ }
1090
+
1091
+ const testScope = new A_Scope({ name: 'TestScope', components: [ComponentA] });
1092
+
1093
+ const feature = new A_Feature({
1094
+ name: 'testFeature',
1095
+ scope: testScope,
1096
+ template: [
1097
+ {
1098
+ name: 'ComponentA.feature1',
1099
+ dependency: new A_Dependency('ComponentA'),
1100
+ handler: 'feature1',
1101
+ },
1102
+ {
1103
+ name: 'ComponentA.feature1',
1104
+ dependency: new A_Dependency('ComponentA'),
1105
+ handler: 'feature1',
1106
+ },
1107
+ ]
1108
+ });
1109
+
1110
+ await feature.process();
1111
+
1112
+ expect(resultChain).toEqual([
1113
+ 'ComponentA.feature1',
1114
+ 'ComponentA.feature1'
1115
+ ]);
1116
+ })
1041
1117
  });