@adaas/a-concept 0.1.46 → 0.1.48

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.1.46",
3
+ "version": "0.1.48",
4
4
  "description": "A-Concept is a framework to build new Applications within or outside the ADAAS ecosystem. This framework is designed to be modular structure regardless environment and program goal.",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./dist/index.cjs",
@@ -0,0 +1,75 @@
1
+
2
+ import { A_Context } from "@adaas/a-concept/global/A-Context/A-Context.class";
3
+ import { A_Meta } from "@adaas/a-concept/global/A-Meta/A-Meta.class";
4
+ import { A_TYPES__ComponentMetaKey } from "@adaas/a-concept/global/A-Component/A-Component.constants";
5
+ import { A_TYPES__ContainerMetaKey } from "@adaas/a-concept/global/A-Container/A-Container.constants";
6
+ import { A_TypeGuards } from "@adaas/a-concept/helpers/A_TypeGuards.helper";
7
+ import { A_TYPES__A_InjectDecorator_Meta, A_TYPES__InjectableTargets } from "../A-Inject/A-Inject.types";
8
+ import { A_TYPES__A_Dependency_ParentDecoratorReturn } from "./A-Dependency.types";
9
+ import { A_DependencyError } from "./A-Dependency.error";
10
+ import { A_CommonHelper } from "@adaas/a-concept/helpers/A_Common.helper";
11
+
12
+
13
+ /**
14
+ * Should indicate which dependency is required
15
+ */
16
+ export function A_Dependency_Parent(
17
+ /**
18
+ * Indicates how many layers up the parent dependency should be resolved from current dependency
19
+ *
20
+ * Default: -1 (one layer up)
21
+ */
22
+ layerOffset: number = -1
23
+ ): A_TYPES__A_Dependency_ParentDecoratorReturn {
24
+
25
+ return function (
26
+ target: A_TYPES__InjectableTargets,
27
+ methodName: string | symbol | undefined,
28
+ parameterIndex: number
29
+ ) {
30
+ // for Error handling purposes
31
+ const componentName = A_CommonHelper.getComponentName(target)
32
+
33
+ if (!A_TypeGuards.isTargetAvailableForInjection(target)) {
34
+ throw new A_DependencyError(
35
+ A_DependencyError.InvalidDependencyTarget,
36
+ `A-Dependency cannot be used on the target of type ${typeof target} (${componentName})`
37
+ );
38
+ }
39
+
40
+ // determine the method name or 'constructor' for constructor injections
41
+ const method = methodName ? String(methodName) : 'constructor';
42
+ let metaKey;
43
+
44
+ switch (true) {
45
+ case A_TypeGuards.isComponentConstructor(target) || A_TypeGuards.isComponentInstance(target):
46
+ metaKey = A_TYPES__ComponentMetaKey.INJECTIONS;
47
+ break;
48
+
49
+ case A_TypeGuards.isContainerInstance(target):
50
+ metaKey = A_TYPES__ContainerMetaKey.INJECTIONS;
51
+ break;
52
+ }
53
+
54
+ // get existing meta or create a new one
55
+ const existedMeta = A_Context.meta(target).get(metaKey) || new A_Meta();
56
+ // get existing injections for the method or create a new array
57
+ const paramsArray: A_TYPES__A_InjectDecorator_Meta = existedMeta.get(method) || [];
58
+
59
+ // set the parameter injection info
60
+ paramsArray[parameterIndex] = {
61
+ ...(paramsArray[parameterIndex] || {}),
62
+ parent: { layerOffset }
63
+ }
64
+ // save back the updated injections array
65
+ existedMeta.set(method, paramsArray);
66
+
67
+ // save back the updated meta info
68
+ A_Context
69
+ .meta(target)
70
+ .set(
71
+ metaKey,
72
+ existedMeta
73
+ );
74
+ }
75
+ }
@@ -1,5 +1,6 @@
1
1
  import { A_Dependency_Default } from "./A-Dependency-Default.decorator";
2
2
  import { A_Dependency_Load } from "./A-Dependency-Load.decorator";
3
+ import { A_Dependency_Parent } from "./A-Dependency-Parent.decorator";
3
4
  import { A_Dependency_Require } from "./A-Dependency-Require.decorator";
4
5
 
5
6
 
@@ -30,4 +31,37 @@ export class A_Dependency {
30
31
  static get Default(): typeof A_Dependency_Default {
31
32
  return A_Dependency_Default;
32
33
  }
34
+ /**
35
+ * Allows to indicate which parent dependency should be resolved
36
+ * e.g. from which layer up the parent should be taken
37
+ *
38
+ * @returns
39
+ */
40
+ static get Parent(): typeof A_Dependency_Parent {
41
+ return A_Dependency_Parent;
42
+ }
43
+
44
+ protected _name: string;
45
+
46
+ /**
47
+ * Class instances allows to indentify dependencies by name and use them for better type checking
48
+ *
49
+ * @param name
50
+ */
51
+ constructor(
52
+ name: string
53
+ ) {
54
+ this._name = name;
55
+ }
56
+
57
+ /**
58
+ * Gets the dependency name
59
+ *
60
+ * Can be identifier, url or any string value
61
+ *
62
+ * @returns
63
+ */
64
+ get name(): string {
65
+ return this._name;
66
+ }
33
67
  }
@@ -25,4 +25,11 @@ export type A_TYPES__A_Dependency_DefaultDecoratorReturn<T = any> = (
25
25
  target: T,
26
26
  propertyKey: string | symbol | undefined,
27
27
  parameterIndex: number
28
+ ) => void
29
+
30
+
31
+ export type A_TYPES__A_Dependency_ParentDecoratorReturn<T = any> = (
32
+ target: T,
33
+ propertyKey: string | symbol | undefined,
34
+ parameterIndex: number
28
35
  ) => void
@@ -8,7 +8,7 @@ import { A_Scope } from "../A-Scope/A-Scope.class";
8
8
  import { A_FormatterHelper } from "@adaas/a-concept/helpers/A_Formatter.helper";
9
9
  import { ASEID } from "../ASEID/ASEID.class";
10
10
  import { A_IdentityHelper } from "@adaas/a-concept/helpers/A_Identity.helper";
11
- import { A_Entity_Error } from "./A-Entity.error";
11
+ import { A_EntityError } from "./A-Entity.error";
12
12
  import { A_Feature } from "../A-Feature/A-Feature.class";
13
13
 
14
14
 
@@ -236,7 +236,7 @@ export class A_Entity<
236
236
  }
237
237
 
238
238
  // none of the above -> throw consistent error
239
- throw new A_Entity_Error(A_Entity_Error.ValidationError, 'Unable to determine A-Entity constructor initialization method. Please check the provided parameters.');
239
+ throw new A_EntityError(A_EntityError.ValidationError, 'Unable to determine A-Entity constructor initialization method. Please check the provided parameters.');
240
240
  }
241
241
 
242
242
 
@@ -2,7 +2,7 @@ import { A_Error } from "../A-Error/A_Error.class";
2
2
 
3
3
 
4
4
 
5
- export class A_Entity_Error extends A_Error {
5
+ export class A_EntityError extends A_Error {
6
6
 
7
7
  /**
8
8
  * Error code for validation errors.
@@ -363,9 +363,10 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
363
363
  scope?: A_Scope,
364
364
  ) {
365
365
  try {
366
- if (scope && !scope.isInheritedFrom(A_Context.scope(this)))
367
- scope.inherit(A_Context.scope(this));
368
-
366
+ // It seems like this is a bad idea to enforce scope inheritance here
367
+ // ---------------------------------------------------------------
368
+ // if (scope && !scope.isInheritedFrom(A_Context.scope(this)))
369
+ // scope.inherit(A_Context.scope(this));
369
370
 
370
371
  if (this.isProcessed)
371
372
  return;
@@ -36,6 +36,9 @@ export type A_TYPES__A_InjectDecorator_Meta = Array<{
36
36
  require?: boolean
37
37
  load?: string
38
38
  defaultArgs?: any,
39
+ parent?: {
40
+ layerOffset?: number
41
+ },
39
42
  create?: boolean
40
43
  instructions?: Partial<A_TYPES__A_InjectDecorator_EntityInjectionInstructions>,
41
44
  }>;
@@ -136,24 +136,39 @@ export class A_Stage {
136
136
  case A_TypeGuards.isCallerConstructor(arg.target):
137
137
  return this._feature.caller.component;
138
138
 
139
- case A_TypeGuards.isScopeConstructor(arg.target):
140
- return scope;
141
-
142
139
  case A_TypeGuards.isFeatureConstructor(arg.target):
143
140
  return this._feature;
144
141
 
145
- case A_TypeGuards.isEntityConstructor(arg.target) && 'instructions' in arg && !!arg.instructions:
146
- return scope.resolve(arg.target, arg.instructions)
147
-
148
142
  default: {
149
- const { target, require, create, defaultArgs } = arg;
143
+ const { target, require, create, defaultArgs, parent } = arg;
144
+
145
+
146
+ let dependency;
147
+ let targetScope = scope;
148
+
149
+ if (parent && typeof parent.layerOffset === 'number') {
150
+ let parentScope = scope.parent;
150
151
 
151
- let dependency = scope.resolve(target as any);
152
+ let offset = parent.layerOffset;
153
+
154
+ while (offset < -1 && parentScope) {
155
+ parentScope = parentScope.parent;
156
+ offset++;
157
+ }
158
+
159
+ if (parentScope) {
160
+ dependency = parentScope.resolve(target);
161
+ targetScope = parentScope;
162
+ }
163
+
164
+ } else {
165
+ dependency = targetScope.resolve(target);
166
+ }
152
167
 
153
168
  if (create && !dependency && A_TypeGuards.isAllowedForDependencyDefaultCreation(target)) {
154
169
  const newDependency = new target(...defaultArgs);
155
170
 
156
- scope.register(newDependency);
171
+ targetScope.register(newDependency);
157
172
  return newDependency;
158
173
  }
159
174
 
@@ -164,7 +179,7 @@ export class A_Stage {
164
179
  );
165
180
  }
166
181
 
167
- return scope.resolve(arg.target)
182
+ return dependency;
168
183
  }
169
184
  }
170
185
  })
@@ -192,11 +207,11 @@ export class A_Stage {
192
207
  break;
193
208
 
194
209
  case A_TypeGuards.isString(component):
195
- instance = scope.resolve(component);
210
+ instance = scope.resolve(component) || this.feature.scope.resolve(component);
196
211
  break;
197
212
 
198
213
  default:
199
- instance = scope.resolve(component);
214
+ instance = scope.resolve(component) || this.feature.scope.resolve(component);
200
215
  break;
201
216
  }
202
217
 
@@ -13,9 +13,11 @@ export class A_FormatterHelper {
13
13
  */
14
14
  static toUpperSnakeCase(str: string): string {
15
15
  return str
16
- .replace(/([a-z])([A-Z])/g, '$1_$2') // Handle lowercase followed by uppercase
17
- .replace(/[-\s]([A-Z])/g, '_$1') // Handle non-alphabetical followed by uppercase
18
- .replace(/-/g, '_')
16
+ .trim()
17
+ .replace(/([a-z])([A-Z])/g, '$1_$2') // Handle camelCase
18
+ .replace(/[^a-zA-Z0-9]+/g, '_') // Replace non-alphanumeric with underscores
19
+ .replace(/_+/g, '_') // Collapse multiple underscores
20
+ .replace(/^_|_$/g, '') // Remove leading/trailing underscores
19
21
  .toUpperCase();
20
22
  }
21
23
  /**
@@ -25,7 +27,18 @@ export class A_FormatterHelper {
25
27
  * @returns
26
28
  */
27
29
  static toCamelCase(str: string): string {
28
- return str.toLowerCase().replace(/_([a-z])/g, (match, letter) => letter.toUpperCase());
30
+ return str
31
+ .trim()
32
+ .replace(/[^a-zA-Z0-9]+/g, ' ') // Replace non-alphanumeric with spaces
33
+ .split(' ') // Split by spaces
34
+ .filter(Boolean) // Remove empty items
35
+ .map((part, index) => {
36
+ if (index === 0) {
37
+ return part.toLowerCase();
38
+ }
39
+ return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
40
+ })
41
+ .join('');
29
42
  }
30
43
  /**
31
44
  * Convert string to PascalCase
@@ -34,8 +47,14 @@ export class A_FormatterHelper {
34
47
  * @returns
35
48
  */
36
49
  static toPascalCase(str: string): string {
37
- const camelCase = this.toCamelCase(str);
38
- return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
50
+ return str
51
+ .trim()
52
+ .replace(/([a-z])([A-Z])/g, '$1 $2') // Insert space before uppercase in camelCase
53
+ .replace(/[^a-zA-Z0-9]+/g, ' ') // Replace non-alphanumeric with spaces
54
+ .split(' ') // Split by spaces
55
+ .filter(Boolean) // Remove empty items
56
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
57
+ .join('');
39
58
  }
40
59
  /**
41
60
  * Convert string to kebab-case
package/src/index.ts CHANGED
@@ -6,27 +6,35 @@ export * from './types/A_Common.types';
6
6
 
7
7
  // ---------------------- Major Components ----------------------
8
8
  export { A_Context } from './global/A-Context/A-Context.class';
9
+ export { A_ContextError } from './global/A-Context/A-Context.error';
9
10
  export * from './global/A-Context/A-Context.types';
10
11
 
11
12
  export { A_Concept } from './global/A-Concept/A-Concept.class';
12
13
  export { A_ConceptMeta } from './global/A-Concept/A-Concept.meta';
13
14
  export * from './global/A-Concept/A-Concept.types';
15
+ export * from './global/A-Concept/A-Concept.constants';
14
16
 
15
17
  export { A_Container } from './global/A-Container/A-Container.class';
16
18
  export { A_ContainerMeta } from './global/A-Container/A-Container.meta';
17
19
  export * from './global/A-Container/A-Container.types';
20
+ export * from './global/A-Container/A-Container.constants';
18
21
 
19
22
  export { A_Component } from './global/A-Component/A-Component.class';
20
23
  export { A_ComponentMeta } from './global/A-Component/A-Component.meta';
21
24
  export * from './global/A-Component/A-Component.types';
25
+ export * from './global/A-Component/A-Component.constants';
22
26
 
23
27
  export { A_Entity } from './global/A-Entity/A-Entity.class';
28
+ export { A_EntityError } from './global/A-Entity/A-Entity.error';
29
+ export { A_EntityMeta } from './global/A-Entity/A-Entity.meta';
24
30
  export * from './global/A-Entity/A-Entity.types';
31
+ export * from './global/A-Entity/A-Entity.constants';
25
32
 
26
33
 
27
34
  // ---------------------- Common Components ----------------------
28
35
  export { A_Abstraction } from './global/A-Abstraction/A-Abstraction.class';
29
36
  export { A_AbstractionError } from './global/A-Abstraction/A-Abstraction.error';
37
+ export { A_Abstraction_Extend } from './global/A-Abstraction/A-Abstraction-Extend.decorator';
30
38
  export * from './global/A-Abstraction/A-Abstraction.types';
31
39
 
32
40
  export { A_Caller } from './global/A-Caller/A_Caller.class';
@@ -35,13 +43,17 @@ export * from './global/A-Caller/A_Caller.types';
35
43
 
36
44
  export { A_Error } from './global/A-Error/A_Error.class';
37
45
  export * from './global/A-Error/A_Error.types';
46
+ export * from './global/A-Error/A_Error.constants';
38
47
 
39
48
  export { ASEID } from './global/ASEID/ASEID.class';
40
49
  export { ASEID_Error } from './global/ASEID/ASEID.error';
41
50
  export * from './global/ASEID/ASEID.types';
51
+ // export * from './global/ASEID/ASEID.constants';
42
52
 
43
53
  export { A_Feature } from './global/A-Feature/A-Feature.class';
44
54
  export { A_FeatureError } from './global/A-Feature/A-Feature.error';
55
+ export { A_Feature_Define } from './global/A-Feature/A-Feature-Define.decorator';
56
+ export { A_Feature_Extend } from './global/A-Feature/A-Feature-Extend.decorator';
45
57
  export * from './global/A-Feature/A-Feature.types';
46
58
 
47
59
  export { A_Stage } from './global/A-Stage/A-Stage.class';
@@ -60,20 +72,14 @@ export * from './global/A-Fragment/A-Fragment.types';
60
72
 
61
73
  export { A_Dependency } from './global/A-Dependency/A-Dependency.class';
62
74
  export { A_DependencyError } from './global/A-Dependency/A-Dependency.error';
75
+ export { A_Dependency_Require } from './global/A-Dependency/A-Dependency-Require.decorator';
76
+ export { A_Dependency_Load } from './global/A-Dependency/A-Dependency-Load.decorator';
77
+ export { A_Dependency_Default } from './global/A-Dependency/A-Dependency-Default.decorator';
63
78
  export * from './global/A-Dependency/A-Dependency.types';
64
79
 
65
-
66
- // =================================================================================================
67
- // =============================== Export Decorators ===============================================
68
- // =================================================================================================
80
+ export { A_InjectError } from './global/A-Inject/A-Inject.error';
69
81
  export { A_Inject } from './global/A-Inject/A-Inject.decorator';
70
82
  export * from './global/A-Inject/A-Inject.types';
71
- export { A_Feature_Define } from './global/A-Feature/A-Feature-Define.decorator';
72
- export { A_Feature_Extend } from './global/A-Feature/A-Feature-Extend.decorator';
73
- export { A_Abstraction_Extend } from './global/A-Abstraction/A-Abstraction-Extend.decorator';
74
- export { A_Dependency_Require } from './global/A-Dependency/A-Dependency-Require.decorator';
75
- export { A_Dependency_Load } from './global/A-Dependency/A-Dependency-Load.decorator';
76
- export { A_Dependency_Default } from './global/A-Dependency/A-Dependency-Default.decorator';
77
83
 
78
84
 
79
85
  // =================================================================================================
@@ -1,6 +1,7 @@
1
1
  import { A_CommonHelper } from "@adaas/a-concept/helpers/A_Common.helper";
2
2
  import { A_IdentityHelper } from "@adaas/a-concept/helpers/A_Identity.helper";
3
3
  import { A_TYPES__DeepPartial } from "@adaas/a-concept/types/A_Common.types";
4
+ import { A_FormatterHelper } from "../src";
4
5
  jest.retryTimes(0);
5
6
 
6
7
  describe('A-Common Tests', () => {
@@ -56,7 +57,7 @@ describe('A-Common Tests', () => {
56
57
  c: {
57
58
  d: string
58
59
  },
59
- bool:{
60
+ bool: {
60
61
  a: boolean
61
62
  },
62
63
  f: (name: string) => string
@@ -69,7 +70,7 @@ describe('A-Common Tests', () => {
69
70
  c: {
70
71
  d: 'd'
71
72
  },
72
- bool:{
73
+ bool: {
73
74
  a: true
74
75
  },
75
76
  f: (name: string) => { return name },
@@ -79,10 +80,10 @@ describe('A-Common Tests', () => {
79
80
  const t2: any = {
80
81
  e: 'foo',
81
82
  b: 'bb',
82
- c:{
83
+ c: {
83
84
  d: 'ddd'
84
85
  },
85
- bool:{
86
+ bool: {
86
87
  a: false
87
88
  },
88
89
  some: {
@@ -114,4 +115,58 @@ describe('A-Common Tests', () => {
114
115
  expect(timestamp).toBeLessThanOrEqual(now);
115
116
  expect(timestamp).toBeGreaterThan(now - 60000); // within the last minute
116
117
  });
118
+
119
+ it('Should translate all strings to Pascal Case', async () => {
120
+ const testStrings = [
121
+ { input: 'hello-world', expected: 'HelloWorld' },
122
+ { input: 'FooBar', expected: 'FooBar' },
123
+ { input: 'foo_bar', expected: 'FooBar' },
124
+ { input: ' leading-trailing ', expected: 'LeadingTrailing' },
125
+ { input: 'multiple--separators__here', expected: 'MultipleSeparatorsHere' },
126
+ { input: 'alreadyPascalCase', expected: 'AlreadyPascalCase' },
127
+ { input: 'single', expected: 'Single' },
128
+ { input: '', expected: '' },
129
+ ];
130
+
131
+ for (const { input, expected } of testStrings) {
132
+ const result = A_FormatterHelper.toPascalCase(input);
133
+ expect(result).toBe(expected);
134
+ }
135
+ });
136
+
137
+ it('Should translate all strings to Upper Snake Case', async () => {
138
+ const testStrings = [
139
+ { input: 'hello-world', expected: 'HELLO_WORLD' },
140
+ { input: 'FOO_BAR', expected: 'FOO_BAR' },
141
+ { input: 'fooBar', expected: 'FOO_BAR' },
142
+ { input: ' leadingTrailing ', expected: 'LEADING_TRAILING' },
143
+ { input: 'multiple--separators__here', expected: 'MULTIPLE_SEPARATORS_HERE' },
144
+ { input: 'already_upper_snake_case', expected: 'ALREADY_UPPER_SNAKE_CASE' },
145
+ { input: 'single', expected: 'SINGLE' },
146
+ { input: '', expected: '' },
147
+ ];
148
+
149
+ for (const { input, expected } of testStrings) {
150
+ const result = A_FormatterHelper.toUpperSnakeCase(input);
151
+ expect(result).toBe(expected);
152
+ }
153
+ });
154
+
155
+ it('Should translate all strings to Kebab Case', async () => {
156
+ const testStrings = [
157
+ { input: 'hello-world', expected: 'hello-world' },
158
+ { input: 'fooBar', expected: 'foo-bar' },
159
+ { input: ' leadingTrailing ', expected: 'leading-trailing' },
160
+ { input: 'kebab-case', expected: 'kebab-case' },
161
+ { input: 'multiple--separators__here', expected: 'multiple-separators-here' },
162
+ { input: 'already_kebab_case', expected: 'already-kebab-case' },
163
+ { input: 'single', expected: 'single' },
164
+ { input: '', expected: '' },
165
+ ];
166
+
167
+ for (const { input, expected } of testStrings) {
168
+ const result = A_FormatterHelper.toKebabCase(input);
169
+ expect(result).toBe(expected);
170
+ }
171
+ });
117
172
  });
@@ -465,19 +465,24 @@ describe('A-Feature tests', () => {
465
465
  executionOrder.push('stepThree');
466
466
  }
467
467
  }
468
+ try {
468
469
 
470
+ // 2) create a running scope
471
+ const scope = new A_Scope({ name: 'TestScope', components: [ComponentA, ComponentB] });
469
472
 
470
- // 2) create a running scope
471
- const scope = new A_Scope({ name: 'TestScope', components: [ComponentA, ComponentB] });
472
473
 
474
+ // 3) create an instance of the component from the scope
475
+ const myComponent = scope.resolve(ComponentA)!;
476
+ expect(myComponent).toBeInstanceOf(ComponentA);
473
477
 
474
- // 3) create an instance of the component from the scope
475
- const myComponent = scope.resolve(ComponentA)!;
476
- expect(myComponent).toBeInstanceOf(ComponentA);
478
+ await myComponent.feature1();
477
479
 
478
- await myComponent.feature1();
480
+ expect(executionOrder).toEqual(['stepOne', 'stepThree']);
481
+
482
+ } catch (error) {
483
+ console.error('Error during feature processing: ', error);
484
+ }
479
485
 
480
- expect(executionOrder).toEqual(['stepOne', 'stepThree']);
481
486
  });
482
487
  it('Should allow to interrupt a new feature', async () => {
483
488
  const feature = new A_Feature({