@adaas/a-concept 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-concept",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
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,74 @@ 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
+ throw 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
+ }
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
+ // Check if feature has been interrupted before processing next stage
401
+ if (this.state === A_TYPES__FeatureState.INTERRUPTED) {
402
+ return;
403
+ }
404
+
405
+ // If we've processed all stages, complete the feature
406
+ if (index >= stages.length) {
407
+ this.completed();
408
+ return;
409
+ }
410
+
411
+ const stage = stages[index];
412
+ const result = stage.process(scope);
413
+
414
+ if (A_TypeGuards.isPromiseInstance(result)) {
415
+ // Async stage - return promise that processes remaining stages
416
+ return result
417
+ .then(() => {
418
+ // Check for interruption after async stage completes
419
+ if (this.state === A_TYPES__FeatureState.INTERRUPTED) {
420
+ return;
421
+ }
422
+ return this.processStagesSequentially(stages, scope, index + 1);
423
+ })
424
+ .catch(error => {
425
+ throw this.failed(new A_FeatureError({
426
+ title: A_FeatureError.FeatureProcessingError,
427
+ description: `An error occurred while processing the A-Feature: ${this.name}. Failed at stage: ${stage.name}.`,
428
+ stage: stage,
429
+ originalError: error
430
+ }));
431
+ });
432
+ } else {
433
+ // Sync stage - continue to next stage immediately
434
+ return this.processStagesSequentially(stages, scope, index + 1);
435
+ }
436
+ } catch (error) {
437
+ throw this.failed(new A_FeatureError({
438
+ title: A_FeatureError.FeatureProcessingError,
439
+ description: `An error occurred while processing the A-Feature: ${this.name}. Failed at stage: ${this.stage?.name || 'N/A'}.`,
440
+ stage: this.stage,
441
+ originalError: error
442
+ }));
443
+ }
390
444
  }
391
445
  /**
392
446
  * This method moves the feature to the next stage
@@ -409,22 +463,27 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
409
463
  * @param result
410
464
  * @returns
411
465
  */
412
- async completed(): Promise<void> {
466
+ completed(): void {
413
467
  if (this.isProcessed) return;
414
468
 
469
+ // Don't complete if interrupted
470
+ if (this.state === A_TYPES__FeatureState.INTERRUPTED) {
471
+ return;
472
+ }
415
473
 
416
474
  this._state = A_TYPES__FeatureState.COMPLETED;
417
475
 
418
476
  this.scope.destroy();
419
477
  }
420
478
  /**
421
- * This method marks the feature as failed and throws an error
479
+ * This method marks the feature as failed and returns the error
422
480
  * Uses to mark the feature as failed
423
481
  *
424
482
  * @param error
483
+ * @returns The error that caused the failure
425
484
  */
426
- async failed(error: A_FeatureError) {
427
- if (this.isProcessed) return;
485
+ failed(error: A_FeatureError): A_FeatureError {
486
+ if (this.isProcessed) return this._error!;
428
487
 
429
488
  this._state = A_TYPES__FeatureState.FAILED;
430
489
 
@@ -432,45 +491,47 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
432
491
 
433
492
  this.scope.destroy();
434
493
 
435
- throw this._error;
494
+ return this._error;
436
495
  }
437
496
  /**
438
- * This method marks the feature as failed and throws an error
497
+ * This method marks the feature as interrupted and throws an error
439
498
  * Uses to interrupt or end the feature processing
440
499
  *
441
500
  * @param error
442
501
  */
443
- async interrupt(
502
+ interrupt(
444
503
  /**
445
504
  * The reason of feature interruption
446
505
  */
447
506
  reason?: string | A_StageError | Error
448
- ) {
449
- if (this.isProcessed) return;
507
+ ): A_FeatureError {
508
+ if (this.isProcessed) return this._error!;
450
509
 
451
510
  this._state = A_TYPES__FeatureState.INTERRUPTED;
452
511
 
453
512
  switch (true) {
454
513
  case A_TypeGuards.isString(reason):
455
- this._error = new A_FeatureError(A_FeatureError.Interruption, reason);
514
+ this._error = new A_FeatureError(A_FeatureError.Interruption, reason as string);
456
515
  break;
457
516
 
458
517
  case A_TypeGuards.isErrorInstance(reason):
459
518
  this._error = new A_FeatureError({
460
519
  code: A_FeatureError.Interruption,
461
- title: reason.title,
462
- description: reason.description,
520
+ title: (reason as any).title || 'Feature Interrupted',
521
+ description: (reason as any).description || (reason as Error).message,
463
522
  stage: this.stage,
464
523
  originalError: reason
465
524
  });
466
525
  break;
467
526
 
468
527
  default:
528
+ this._error = new A_FeatureError(A_FeatureError.Interruption, 'Feature was interrupted');
469
529
  break;
470
530
  }
471
531
 
472
-
473
532
  this.scope.destroy();
533
+
534
+ return this._error;
474
535
  }
475
536
 
476
537
 
@@ -530,7 +591,17 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
530
591
  // create new caller for the chained feature
531
592
  feature._caller = this._caller;
532
593
 
533
- return feature.process(featureScope);
594
+ const result = feature.process(featureScope);
595
+
596
+ // If the chained feature processing returns a promise, ensure errors are propagated
597
+ if (A_TypeGuards.isPromiseInstance(result)) {
598
+ return result.catch(error => {
599
+ // Re-throw to ensure chained feature errors propagate to caller
600
+ throw error;
601
+ });
602
+ }
603
+
604
+ return result;
534
605
  }
535
606
 
536
607
 
@@ -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
  });