@adaas/a-concept 0.2.5 → 0.2.6

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.2.5",
3
+ "version": "0.2.6",
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
  "main": "./dist/index.cjs",
@@ -29,7 +29,7 @@ export class A_Component {
29
29
  * @param scope - the scope in which to call the feature
30
30
  * @returns - void
31
31
  */
32
- async call(
32
+ call(
33
33
  /**
34
34
  * Name of the feature to call
35
35
  */
@@ -38,13 +38,13 @@ export class A_Component {
38
38
  * Scope in which the feature will be executed
39
39
  */
40
40
  scope?: A_Scope
41
- ) {
41
+ ): Promise<any> | void {
42
42
  const newFeature = new A_Feature({
43
43
  name: feature,
44
44
  component: this
45
45
  });
46
46
 
47
- return await newFeature.process(scope);
47
+ return newFeature.process(scope);
48
48
  }
49
49
  }
50
50
 
@@ -355,13 +355,13 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
355
355
  * This method processes the feature by executing all the stages
356
356
  *
357
357
  */
358
- async process(
358
+ process(
359
359
  /**
360
360
  * Optional scope to be used to resolve the steps dependencies
361
361
  * If not provided, the scope of the caller component will be used
362
362
  */
363
363
  scope?: A_Scope,
364
- ) {
364
+ ): Promise<void> | void {
365
365
  try {
366
366
  // It seems like this is a bad idea to enforce scope inheritance here
367
367
  // ---------------------------------------------------------------
@@ -373,20 +373,63 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
373
373
 
374
374
  this._state = A_TYPES__FeatureState.PROCESSING;
375
375
 
376
- for (const stage of this) {
377
- await stage.process(scope);
378
- }
376
+ // Convert iterator to array to get all stages
377
+ const stages = Array.from(this);
378
+
379
+ return this.processStagesSequentially(stages, scope, 0);
379
380
 
380
- return await this.completed();
381
381
  } catch (error) {
382
- return await this.failed(new A_FeatureError({
382
+ this.failed(new A_FeatureError({
383
383
  title: A_FeatureError.FeatureProcessingError,
384
384
  description: `An error occurred while processing the A-Feature: ${this.name}. Failed at stage: ${this.stage?.name || 'N/A'}.`,
385
385
  stage: this.stage,
386
386
  originalError: error
387
387
  }));
388
388
  }
389
+ }
390
+
391
+ /**
392
+ * Process stages one by one, ensuring each stage completes before starting the next
393
+ */
394
+ private processStagesSequentially(
395
+ stages: A_Stage[],
396
+ scope: A_Scope | undefined,
397
+ index: number
398
+ ): Promise<void> | void {
399
+ try {
400
+ // If we've processed all stages, complete the feature
401
+ if (index >= stages.length) {
402
+ this.completed();
403
+ return;
404
+ }
389
405
 
406
+ const stage = stages[index];
407
+ const result = stage.process(scope);
408
+
409
+ if (A_TypeGuards.isPromiseInstance(result)) {
410
+ // Async stage - return promise that processes remaining stages
411
+ return result.then(() => {
412
+ return this.processStagesSequentially(stages, scope, index + 1);
413
+ }).catch(error => {
414
+ this.failed(new A_FeatureError({
415
+ title: A_FeatureError.FeatureProcessingError,
416
+ description: `An error occurred while processing the A-Feature: ${this.name}. Failed at stage: ${stage.name}.`,
417
+ stage: stage,
418
+ originalError: error
419
+ }));
420
+ });
421
+ } else {
422
+ // Sync stage - continue to next stage immediately
423
+ return this.processStagesSequentially(stages, scope, index + 1);
424
+ }
425
+ } catch (error) {
426
+ this.failed(new A_FeatureError({
427
+ title: A_FeatureError.FeatureProcessingError,
428
+ description: `An error occurred while processing the A-Feature: ${this.name}. Failed at stage: ${this.stage?.name || 'N/A'}.`,
429
+ stage: this.stage,
430
+ originalError: error
431
+ }));
432
+ }
390
433
  }
391
434
  /**
392
435
  * This method moves the feature to the next stage
@@ -409,7 +452,7 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
409
452
  * @param result
410
453
  * @returns
411
454
  */
412
- async completed(): Promise<void> {
455
+ completed(): void {
413
456
  if (this.isProcessed) return;
414
457
 
415
458
 
@@ -423,7 +466,7 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
423
466
  *
424
467
  * @param error
425
468
  */
426
- async failed(error: A_FeatureError) {
469
+ failed(error: A_FeatureError) {
427
470
  if (this.isProcessed) return;
428
471
 
429
472
  this._state = A_TYPES__FeatureState.FAILED;
@@ -35,11 +35,6 @@ export class A_Stage {
35
35
  */
36
36
  private _status: A_TYPES__A_Stage_Status = A_TYPES__A_Stage_Status.INITIALIZED;
37
37
 
38
- /**
39
- * Promise that will be resolved when the stage is Processed
40
- */
41
- private _processed: Promise<void> | undefined;
42
-
43
38
 
44
39
  /**
45
40
  * A_Stage is a callable A_Function within A_Feature that should be run with specific parameters.
@@ -106,7 +101,7 @@ export class A_Stage {
106
101
  * @param step
107
102
  * @returns
108
103
  */
109
- protected async getStepArgs(
104
+ protected getStepArgs(
110
105
  scope: A_Scope,
111
106
  step: A_TYPES__A_StageStep
112
107
  ) {
@@ -114,24 +109,22 @@ export class A_Stage {
114
109
  (step.dependency.target as A_TYPES__Container_Constructor | A_TYPES__Component_Constructor)
115
110
  || scope.resolveConstructor(step.dependency.name);
116
111
 
117
- return Promise
118
- .all(A_Context
119
- .meta(resolverConstructor)
120
- .injections(step.handler)
121
- .map(async dependency => {
122
- switch (true) {
123
- case A_TypeGuards.isCallerConstructor(dependency.target):
124
- return this._feature.caller.component;
112
+ return A_Context
113
+ .meta(resolverConstructor)
114
+ .injections(step.handler)
115
+ .map(dependency => {
116
+ switch (true) {
117
+ case A_TypeGuards.isCallerConstructor(dependency.target):
118
+ return this._feature.caller.component;
125
119
 
126
- case A_TypeGuards.isFeatureConstructor(dependency.target):
127
- return this._feature;
120
+ case A_TypeGuards.isFeatureConstructor(dependency.target):
121
+ return this._feature;
128
122
 
129
- default: {
130
- return scope.resolve(dependency);
131
- }
123
+ default: {
124
+ return scope.resolve(dependency);
132
125
  }
133
- })
134
- )
126
+ }
127
+ })
135
128
  }
136
129
 
137
130
 
@@ -174,17 +167,23 @@ export class A_Stage {
174
167
  * @param step
175
168
  * @returns
176
169
  */
177
- protected async callStepHandler(
170
+ protected callStepHandler(
178
171
  step: A_TYPES__A_StageStep,
179
172
  scope: A_Scope
180
- ) {
173
+ ): {
174
+ handler: Function,
175
+ params: any[]
176
+ } {
181
177
  // 1) Resolve component
182
- const component = await this.getStepComponent(scope, step);
178
+ const component = this.getStepComponent(scope, step);
183
179
  // 2) Resolve arguments
184
- const callArgs = await this.getStepArgs(scope, step);
180
+ const callArgs = this.getStepArgs(scope, step);
185
181
 
186
182
  // 3) Call handler
187
- return await component[step.handler](...callArgs);
183
+ return {
184
+ handler: component[step.handler].bind(component),
185
+ params: callArgs
186
+ }
188
187
  }
189
188
 
190
189
 
@@ -198,48 +197,51 @@ export class A_Stage {
198
197
  *
199
198
  * @param scope - Scope to be used to resolve the steps dependencies
200
199
  */
201
- async process(
200
+ process(
202
201
  /**
203
202
  * Scope to be used to resolve the steps dependencies
204
203
  */
205
204
  scope?: A_Scope,
206
- ): Promise<void> {
205
+ ): Promise<void> | void {
207
206
 
208
207
  const targetScope = A_TypeGuards.isScopeInstance(scope)
209
208
  ? scope
210
209
  : this._feature.scope;
211
210
 
212
- if (!this._processed)
213
- this._processed = new Promise<void>(
214
- async (resolve, reject) => {
215
- try {
216
- this._status = A_TYPES__A_Stage_Status.PROCESSING;
217
-
218
- if (this._definition.behavior === 'sync') {
219
- // in case we have to wait for the result
220
- await this.callStepHandler(this._definition, targetScope);
221
- } else {
222
- // in case we don't have to wait for the result
223
- this.callStepHandler(this._definition, targetScope);
224
- }
211
+ if (!this.isProcessed) {
212
+ this._status = A_TYPES__A_Stage_Status.PROCESSING;
213
+
214
+ const { handler, params } = this.callStepHandler(this._definition, targetScope);
225
215
 
226
- this.completed();
216
+ const result = handler(...params);
227
217
 
228
- return resolve();
229
- } catch (error) {
230
- const wrappedError = new A_Error(error as any);
218
+ if (A_TypeGuards.isPromiseInstance(result)) {
231
219
 
232
- this.failed(wrappedError);
220
+ return new Promise<void>(
221
+ async (resolve, reject) => {
222
+ try {
223
+ await result;
224
+
225
+ this.completed();
233
226
 
234
- if (this._definition.throwOnError) {
235
227
  return resolve();
236
- } else {
237
- return reject(wrappedError);
228
+ } catch (error) {
229
+ const wrappedError = new A_Error(error as any);
230
+
231
+ this.failed(wrappedError);
232
+
233
+ if (this._definition.throwOnError) {
234
+ return resolve();
235
+ } else {
236
+ return reject(wrappedError);
237
+ }
238
238
  }
239
- }
240
- });
239
+ });
240
+ } else {
241
+ this.completed();
242
+ }
243
+ }
241
244
 
242
- return this._processed;
243
245
  }
244
246
 
245
247
 
@@ -367,4 +367,9 @@ export class A_TypeGuards {
367
367
  static isErrorSerializedType<T extends A_TYPES__Error_Serialized>(param: any): param is T {
368
368
  return !!param && A_TypeGuards.isObject(param) && !(param instanceof Error) && "aseid" in param && ASEID.isASEID(param.aseid);
369
369
  }
370
+
371
+
372
+ static isPromiseInstance<T>(value: any): value is Promise<T> {
373
+ return value instanceof Promise;
374
+ }
370
375
  }
@@ -334,8 +334,6 @@ describe('A-Abstraction Tests', () => {
334
334
 
335
335
  await concept.load();
336
336
 
337
- console.log(executionOrder);
338
-
339
337
  expect(executionOrder).toEqual(['stepTwo']);
340
338
  });
341
339
  });
@@ -644,7 +644,7 @@ describe('A-Feature tests', () => {
644
644
  await baseEntity.test();
645
645
 
646
646
  expect(executionResults).toEqual(['testMethod']);
647
-
647
+
648
648
  await myEntity.test();
649
649
 
650
650
  expect(executionResults).toEqual(['testMethod', 'testMethod']);
@@ -783,4 +783,89 @@ describe('A-Feature tests', () => {
783
783
  ]);
784
784
 
785
785
  })
786
+ it('Should execute Sync operations properly', async () => {
787
+
788
+ const resultChain: string[] = [];
789
+
790
+
791
+ class ChildComponent_A extends A_Component {
792
+ @A_Feature.Extend({
793
+ name: 'testFeature',
794
+ })
795
+ test1() {
796
+ resultChain.push('ChildComponent_A.test');
797
+ }
798
+ }
799
+
800
+ class ChildComponent_B extends A_Component {
801
+ @A_Feature.Extend({
802
+ name: 'testFeature',
803
+ })
804
+ test2() {
805
+ resultChain.push('ChildComponent_B.test');
806
+ }
807
+ }
808
+
809
+
810
+ const testScope = new A_Scope({ name: 'TestScope', components: [ChildComponent_A, ChildComponent_B] });
811
+
812
+ testScope.resolve(ChildComponent_A)!.call('testFeature');
813
+
814
+
815
+ expect(resultChain).toEqual([
816
+ 'ChildComponent_A.test',
817
+ 'ChildComponent_B.test'
818
+ ]);
819
+
820
+ })
821
+
822
+ it('Should execute Async operations properly', async () => {
823
+
824
+ const resultChain: string[] = [];
825
+
826
+
827
+ class ChildComponent_A extends A_Component {
828
+ @A_Feature.Extend({
829
+ name: 'testFeature',
830
+ })
831
+ async test1() {
832
+ resultChain.push('ChildComponent_A.test');
833
+
834
+ await new Promise<void>(async (resolve) => {
835
+ setTimeout(() => {
836
+ resolve();
837
+ }, 3000);
838
+ });
839
+ }
840
+ }
841
+
842
+ class ChildComponent_B extends A_Component {
843
+ @A_Feature.Extend({
844
+ name: 'testFeature',
845
+ })
846
+ async test2() {
847
+ resultChain.push('ChildComponent_B.test');
848
+ }
849
+ }
850
+
851
+ const testScope = new A_Scope({ name: 'TestScope', components: [ChildComponent_A, ChildComponent_B] });
852
+
853
+ await Promise.all([
854
+ new Promise<void>(async (resolve) => {
855
+ setTimeout(() => {
856
+ resultChain.push('feature3');
857
+
858
+ resolve();
859
+ }, 2000);
860
+ }),
861
+ testScope.resolve(ChildComponent_A)!.call('testFeature')
862
+ ]);
863
+
864
+ expect(resultChain).toEqual([
865
+ 'ChildComponent_A.test',
866
+ 'feature3',
867
+ 'ChildComponent_B.test'
868
+ ]);
869
+
870
+ })
786
871
  });