@adaas/a-concept 0.3.7 → 0.3.8

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.7",
3
+ "version": "0.3.8",
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": {
@@ -58,6 +58,8 @@
58
58
  "bench": "ts-node -r tsconfig-paths/register benchmarks/run-all.ts",
59
59
  "bench:step-manager": "ts-node -r tsconfig-paths/register benchmarks/step-manager.bench.ts",
60
60
  "bench:feature-template": "ts-node -r tsconfig-paths/register benchmarks/feature-template.bench.ts",
61
+ "bench:feature-optimize": "ts-node -r tsconfig-paths/register benchmarks/feature-optimize.bench.ts",
62
+ "bench:feature-chaining": "ts-node -r tsconfig-paths/register benchmarks/feature-chaining.bench.ts",
61
63
  "bench:scope-resolve": "ts-node -r tsconfig-paths/register benchmarks/scope-resolve.bench.ts",
62
64
  "bench:feature-lifecycle": "ts-node -r tsconfig-paths/register benchmarks/feature-lifecycle.bench.ts",
63
65
  "start": "nodemon ./tests/example-usage.ts",
@@ -269,17 +269,17 @@ export class A_Entity<
269
269
  * @param lifecycleMethod
270
270
  * @param args
271
271
  */
272
- async call(
272
+ call(
273
273
  feature: string,
274
274
  scope?: A_Scope
275
- ) {
275
+ ): Promise<any> | void {
276
276
  const newFeature = new A_Feature({
277
277
  name: feature,
278
278
  component: this,
279
279
  scope
280
280
  });
281
281
 
282
- return await newFeature.process(scope);
282
+ return newFeature.process(scope);
283
283
  }
284
284
 
285
285
 
@@ -12,7 +12,7 @@ import {
12
12
  A_StageError
13
13
  } from "@adaas/a-concept/a-stage";
14
14
  import { A_StepsManager } from "@adaas/a-concept/a-step-manager";
15
- import { A_TypeGuards} from "@adaas/a-concept/helpers/A_TypeGuards.helper";
15
+ import { A_TypeGuards } from "@adaas/a-concept/helpers/A_TypeGuards.helper";
16
16
  import { A_FeatureError } from "./A-Feature.error";
17
17
  import { A_Context } from "@adaas/a-concept/a-context";
18
18
  import { A_Caller } from "@adaas/a-concept/a-caller";
@@ -203,7 +203,7 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
203
203
  if (!params || typeof params !== 'object') {
204
204
  throw new A_FeatureError(
205
205
  A_FeatureError.FeatureInitializationError,
206
- `Invalid A-Feature initialization parameters of type: ${typeof params} with value: ${JSON.stringify(params).slice(0, 100)}...`
206
+ `Invalid A-Feature initialization parameters of type: ${typeof params} with value: ${JSON.stringify(params)?.slice(0, 100)}...`
207
207
  );
208
208
  }
209
209
  }
@@ -226,7 +226,7 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
226
226
  default:
227
227
  throw new A_FeatureError(
228
228
  A_FeatureError.FeatureInitializationError,
229
- `Invalid A-Feature initialization parameters of type: ${typeof params} with value: ${JSON.stringify(params).slice(0, 100)}...`
229
+ `Invalid A-Feature initialization parameters of type: ${typeof params} with value: ${JSON.stringify(params)?.slice(0, 100)}...`
230
230
  );
231
231
  }
232
232
  }
@@ -241,14 +241,14 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
241
241
  if (!params.template || !Array.isArray(params.template)) {
242
242
  throw new A_FeatureError(
243
243
  A_FeatureError.FeatureInitializationError,
244
- `Invalid A-Feature template provided of type: ${typeof params.template} with value: ${JSON.stringify(params.template).slice(0, 100)}...`
244
+ `Invalid A-Feature template provided of type: ${typeof params.template} with value: ${JSON.stringify(params.template)?.slice(0, 100)}...`
245
245
  );
246
246
  }
247
247
 
248
248
  if (!params.component && (!params.scope || !(params.scope instanceof A_Scope))) {
249
249
  throw new A_FeatureError(
250
250
  A_FeatureError.FeatureInitializationError,
251
- `Invalid A-Feature scope provided of type: ${typeof params.scope} with value: ${JSON.stringify(params.scope).slice(0, 100)}...`
251
+ `Invalid A-Feature scope provided of type: ${typeof params.scope} with value: ${JSON.stringify(params.scope)?.slice(0, 100)}...`
252
252
  );
253
253
  }
254
254
 
@@ -304,7 +304,7 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
304
304
  if (!params.component || !A_TypeGuards.isAllowedForFeatureDefinition(params.component)) {
305
305
  throw new A_FeatureError(
306
306
  A_FeatureError.FeatureInitializationError,
307
- `Invalid A-Feature component provided of type: ${typeof params.component} with value: ${JSON.stringify(params.component).slice(0, 100)}...`
307
+ `Invalid A-Feature component provided of type: ${typeof params.component} with value: ${JSON.stringify(params.component)?.slice(0, 100)}...`
308
308
  );
309
309
  }
310
310
 
@@ -398,52 +398,57 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
398
398
  scope: A_Scope | undefined,
399
399
  index: number
400
400
  ): Promise<void> | void {
401
- try {
402
- // Check if feature has been interrupted before processing next stage
403
- if (this.state === A_TYPES__FeatureState.INTERRUPTED) {
404
- return;
405
- }
406
401
 
407
- // If we've processed all stages, complete the feature
408
- if (index >= stages.length) {
409
- this.completed();
410
- return;
411
- }
402
+ // ── Sync loop avoids stack overflow for all-sync chains ────────────────
403
+ while (index < stages.length) {
404
+
405
+ if (this.state === A_TYPES__FeatureState.INTERRUPTED) return
412
406
 
413
- const stage = stages[index];
414
- const result = stage.process(scope);
407
+ const stage = stages[index]
408
+ let result: Promise<void> | void
415
409
 
410
+ try {
411
+ result = stage.process(scope)
412
+ } catch (error) {
413
+ throw this.createStageError(error, stage)
414
+ }
415
+
416
+ // ── Async stage — hand off to promise chain ───────────────────────────
416
417
  if (A_TypeGuards.isPromiseInstance(result)) {
417
- // Async stage - return promise that processes remaining stages
418
418
  return result
419
419
  .then(() => {
420
- // Check for interruption after async stage completes
421
- if (this.state === A_TYPES__FeatureState.INTERRUPTED) {
422
- return;
423
- }
424
- return this.processStagesSequentially(stages, scope, index + 1);
420
+ if (this.state === A_TYPES__FeatureState.INTERRUPTED) return
421
+ return this.processStagesSequentially(stages, scope, index + 1)
425
422
  })
426
423
  .catch(error => {
427
- throw this.failed(new A_FeatureError({
428
- title: A_FeatureError.FeatureProcessingError,
429
- description: `An error occurred while processing the A-Feature: ${this.name}. Failed at stage: ${stage.name}.`,
430
- stage: stage,
431
- originalError: error
432
- }));
433
- });
434
- } else {
435
- // Sync stage - continue to next stage immediately
436
- return this.processStagesSequentially(stages, scope, index + 1);
424
+ throw this.createStageError(error, stage)
425
+ })
437
426
  }
438
- } catch (error) {
439
- throw this.failed(new A_FeatureError({
440
- title: A_FeatureError.FeatureProcessingError,
441
- description: `An error occurred while processing the A-Feature: ${this.name}. Failed at stage: ${this.stage?.name || 'N/A'}.`,
442
- stage: this.stage,
443
- originalError: error
444
- }));
427
+
428
+ index++
429
+ }
430
+
431
+ // ── All stages complete ───────────────────────────────────────────────────
432
+ if (this.state !== A_TYPES__FeatureState.INTERRUPTED) {
433
+ this.completed()
445
434
  }
446
435
  }
436
+
437
+ private createStageError(error: unknown, stage: A_Stage): A_FeatureError {
438
+ this.failed(new A_FeatureError({
439
+ title: A_FeatureError.FeatureProcessingError,
440
+ description: `An error occurred while processing the A-Feature: ${this.name}. Failed at stage: ${stage.name}.`,
441
+ stage,
442
+ originalError: error,
443
+ }))
444
+
445
+ return new A_FeatureError({
446
+ title: A_FeatureError.FeatureProcessingError,
447
+ description: `An error occurred while processing the A-Feature: ${this.name}. Failed at stage: ${stage.name}.`,
448
+ stage,
449
+ originalError: error,
450
+ })
451
+ }
447
452
  /**
448
453
  * This method moves the feature to the next stage
449
454
  *
@@ -1103,6 +1103,107 @@ describe('A-Feature tests', () => {
1103
1103
  'ComponentA.feature1'
1104
1104
  ]);
1105
1105
  })
1106
+ it('Should return promise if at least one step is async', async () => {
1107
+
1108
+ const resultChain: string[] = [];
1109
+
1110
+ class ComponentA extends A_Component {
1111
+ @A_Feature.Extend({
1112
+ name: 'testFeature',
1113
+ })
1114
+ async step1() {
1115
+ resultChain.push('ComponentA.step1');
1116
+ }
1117
+
1118
+ @A_Feature.Extend({
1119
+ name: 'testFeature',
1120
+ })
1121
+ step2() {
1122
+ resultChain.push('ComponentA.step2');
1123
+ }
1124
+ }
1125
+
1126
+
1127
+ class MyEntity extends A_Entity {
1128
+ async test() {
1129
+ return await this.call('testFeature');
1130
+ }
1131
+ }
1132
+
1133
+
1134
+ const testScope = new A_Scope({ name: 'TestScope', components: [ComponentA] });
1135
+
1136
+ const component = testScope.resolve(ComponentA)!;
1137
+
1138
+ const myEntity = new MyEntity();
1139
+
1140
+ testScope.register(myEntity);
1141
+
1142
+ const res = component.call('testFeature');
1143
+ const res2 = myEntity.test();
1144
+
1145
+ expect(res).toBeInstanceOf(Promise);
1146
+ expect(res2).toBeInstanceOf(Promise);
1147
+
1148
+ await res;
1149
+ await res2;
1150
+
1151
+
1152
+ expect(resultChain).toEqual([
1153
+ 'ComponentA.step1',
1154
+ 'ComponentA.step1',
1155
+ 'ComponentA.step2',
1156
+ 'ComponentA.step2'
1157
+ ]);
1158
+ })
1159
+ it('Should execute sync if all steps are synchronous', async () => {
1160
+
1161
+ const resultChain: string[] = [];
1162
+
1163
+ class ComponentA extends A_Component {
1164
+ @A_Feature.Extend({
1165
+ name: 'testFeature',
1166
+ })
1167
+ step1() {
1168
+ resultChain.push('ComponentA.step1');
1169
+ }
1170
+
1171
+ @A_Feature.Extend({
1172
+ name: 'testFeature',
1173
+ })
1174
+ step2() {
1175
+ resultChain.push('ComponentA.step2');
1176
+ }
1177
+ }
1178
+
1179
+
1180
+ class MyEntity extends A_Entity {
1181
+ test() {
1182
+ return this.call('testFeature');
1183
+ }
1184
+ }
1185
+
1186
+ const testScope = new A_Scope({ name: 'TestScope', components: [ComponentA] });
1187
+
1188
+ const component = testScope.resolve(ComponentA)!;
1189
+
1190
+ const myEntity = new MyEntity();
1191
+
1192
+ testScope.register(myEntity);
1193
+
1194
+ const res = component.call('testFeature');
1195
+ const res2 = myEntity.test();
1196
+
1197
+ expect(res).not.toBeInstanceOf(Promise);
1198
+ expect(res2).not.toBeInstanceOf(Promise);
1199
+
1200
+ expect(resultChain).toEqual([
1201
+ 'ComponentA.step1',
1202
+ 'ComponentA.step2',
1203
+ 'ComponentA.step1',
1204
+ 'ComponentA.step2'
1205
+ ]);
1206
+ })
1106
1207
 
1107
1208
 
1108
1209
  });