@adaas/a-concept 0.1.44 → 0.1.46

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.44",
3
+ "version": "0.1.46",
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",
@@ -35,7 +35,6 @@ import { A_TYPES__Component_Constructor } from "../A-Component/A-Component.types
35
35
  import { A_FormatterHelper } from "@adaas/a-concept/helpers/A_Formatter.helper";
36
36
  import { A_Fragment } from "../A-Fragment/A-Fragment.class";
37
37
  import { A_TYPES__InjectableTargets } from "../A-Inject/A-Inject.types";
38
- import { A_TYPES__ConceptAbstraction } from "../A-Concept/A-Concept.types";
39
38
  import { A_TYPES__ConceptAbstractions } from "../A-Concept/A-Concept.constants";
40
39
  import { A_CommonHelper } from "@adaas/a-concept/helpers/A_Common.helper";
41
40
 
@@ -5,6 +5,9 @@ import { A_TypeGuards } from "@adaas/a-concept/helpers/A_TypeGuards.helper";
5
5
  import { A_TYPES__ComponentMetaKey } from "../A-Component/A-Component.constants";
6
6
  import { A_FeatureError } from "./A-Feature.error";
7
7
  import { A_CommonHelper } from "@adaas/a-concept/helpers/A_Common.helper";
8
+ import { A_TYPES__EntityMetaKey } from "../A-Entity/A-Entity.constants";
9
+ import { A_TYPES__ContainerMetaKey } from "../A-Container/A-Container.constants";
10
+ import { A_Container } from "../A-Container/A-Container.class";
8
11
 
9
12
 
10
13
 
@@ -81,8 +84,21 @@ export function A_Feature_Extend(
81
84
  let include: Array<A_TYPES__FeatureExtendDecoratorScopeItem> = [];
82
85
  let exclude: Array<A_TYPES__FeatureExtendDecoratorScopeItem> = [];
83
86
  let throwOnError: boolean = true;
87
+ let metaKey;
84
88
 
85
89
 
90
+ switch (true) {
91
+ case A_TypeGuards.isEntityInstance(target):
92
+ metaKey = A_TYPES__EntityMetaKey.EXTENSIONS;
93
+ break;
94
+ case A_TypeGuards.isContainerInstance(target):
95
+ metaKey = A_TYPES__ContainerMetaKey.EXTENSIONS
96
+ break;
97
+ case A_TypeGuards.isComponentInstance(target):
98
+ metaKey = A_TYPES__ComponentMetaKey.EXTENSIONS
99
+ break;
100
+ }
101
+
86
102
 
87
103
  switch (true) {
88
104
  case A_TypeGuards.isRegExp(param1):
@@ -130,17 +146,20 @@ export function A_Feature_Extend(
130
146
  }
131
147
 
132
148
 
149
+
150
+
151
+
133
152
  const existedDefinitions = A_Context
134
153
  .meta(target)
135
- .get(A_TYPES__ComponentMetaKey.FEATURES);
154
+ .get(metaKey);
136
155
 
137
156
 
138
157
  // Get the existed metadata or create a new one
139
158
  const meta = A_Context
140
159
  .meta(target)
141
160
 
142
- const existedMeta = meta.get(A_TYPES__ComponentMetaKey.EXTENSIONS)
143
- ? new A_Meta().from(meta.get(A_TYPES__ComponentMetaKey.EXTENSIONS)!)
161
+ const existedMeta = meta.get(metaKey)
162
+ ? new A_Meta().from(meta.get(metaKey)!)
144
163
  : new A_Meta();
145
164
 
146
165
  if (existedDefinitions
@@ -187,7 +206,7 @@ export function A_Feature_Extend(
187
206
  // Update the metadata of the container with the new Feature definition
188
207
  A_Context
189
208
  .meta(target)
190
- .set(A_TYPES__ComponentMetaKey.EXTENSIONS, existedMeta);
209
+ .set(metaKey, existedMeta);
191
210
  };
192
211
  }
193
212
 
@@ -24,7 +24,7 @@ import { A_TYPES__Fragment_Init, A_TYPES__Fragment_Serialized } from "./A-Fragme
24
24
  * constructor() {
25
25
  * super({ name: 'UserFragment' });
26
26
  * }
27
- * }
27
+ * }
28
28
  *
29
29
  * // Custom serialization
30
30
  * class SessionFragment extends A_Fragment<
@@ -41,8 +41,7 @@ import { A_TYPES__Fragment_Init, A_TYPES__Fragment_Serialized } from "./A-Fragme
41
41
  * ```
42
42
  */
43
43
  export class A_Fragment<
44
- _MetaItems extends Record<string, any> = any,
45
- _SerializedType extends A_TYPES__Fragment_Serialized = A_TYPES__Fragment_Serialized & _MetaItems
44
+ _SerializedType extends A_TYPES__Fragment_Serialized = A_TYPES__Fragment_Serialized
46
45
  > {
47
46
  /**
48
47
  * The unique identifier/name for this fragment instance.
@@ -50,14 +49,6 @@ export class A_Fragment<
50
49
  */
51
50
  protected _name: string;
52
51
 
53
- /**
54
- * Internal meta storage using A_Meta for type-safe key-value operations.
55
- * This stores all the fragment's runtime data that can be accessed and modified
56
- * throughout the execution pipeline.
57
- */
58
- protected _meta: A_Meta<_MetaItems> = new A_Meta<_MetaItems>();
59
-
60
-
61
52
  /**
62
53
  * Creates a new A_Fragment instance.
63
54
  *
@@ -69,7 +60,7 @@ export class A_Fragment<
69
60
  * Key Benefits:
70
61
  * - Centralized state management for related operations
71
62
  * - Type-safe meta operations with full IntelliSense support
72
- * - Serialization support for data persistence
63
+ * - Serialization support for data persistence
73
64
  * - Singleton pattern ensures consistent state within scope
74
65
  *
75
66
  * @param params - Initialization parameters
@@ -96,191 +87,6 @@ export class A_Fragment<
96
87
  return this._name;
97
88
  }
98
89
 
99
- /**
100
- * Gets direct access to the underlying Meta object for advanced meta operations.
101
- *
102
- * Use this when you need to perform bulk operations or access Meta-specific methods.
103
- * For simple get/set operations, prefer using the direct methods on the fragment.
104
- *
105
- * @returns The Meta instance containing the fragment's meta
106
- *
107
- * @example
108
- * ```typescript
109
- * const fragment = new A_Fragment<{ users: string[], count: number }>();
110
- *
111
- * // Advanced operations using meta
112
- * fragment.meta.setMultiple({
113
- * users: ['alice', 'bob'],
114
- * count: 2
115
- * });
116
- *
117
- * // Get all keys
118
- * const keys = fragment.meta.keys();
119
- * ```
120
- */
121
- get meta(): A_Meta<_MetaItems> {
122
- return this._meta;
123
- }
124
-
125
- /**
126
- * Checks if a specific meta key exists in the fragment.
127
- *
128
- * @param param - The key to check for existence
129
- * @returns True if the key exists, false otherwise
130
- *
131
- * @example
132
- * ```typescript
133
- * if (fragment.has('userId')) {
134
- * console.log('User ID is set');
135
- * }
136
- * ```
137
- */
138
- has(param: keyof _MetaItems): boolean {
139
- return this._meta.has(param);
140
- }
141
-
142
- /**
143
- * Retrieves a value from the fragment's meta.
144
- *
145
- * @param param - The key to retrieve
146
- * @returns The value associated with the key, or undefined if not found
147
- *
148
- * @example
149
- * ```typescript
150
- * const userId = fragment.get('userId');
151
- * if (userId) {
152
- * console.log(`Current user: ${userId}`);
153
- * }
154
- * ```
155
- */
156
- get(param: keyof _MetaItems): _MetaItems[typeof param] | undefined {
157
- return this._meta.get(param);
158
- }
159
-
160
- /**
161
- * Stores a value in the fragment's meta.
162
- *
163
- * @param param - The key to store the value under
164
- * @param value - The value to store
165
- *
166
- * @example
167
- * ```typescript
168
- * fragment.set('userId', '12345');
169
- * fragment.set('role', 'admin');
170
- * ```
171
- */
172
- set(param: keyof _MetaItems, value: _MetaItems[typeof param]): void {
173
- this._meta.set(param, value);
174
- }
175
-
176
- /**
177
- * Removes a specific key from the fragment's meta.
178
- *
179
- * @param param - The key to remove
180
- *
181
- * @example
182
- * ```typescript
183
- * fragment.drop('temporaryData');
184
- * ```
185
- */
186
- drop(param: keyof _MetaItems): void {
187
- this._meta.delete(param);
188
- }
189
-
190
- /**
191
- * Clears all data from the fragment's meta.
192
- *
193
- * Use with caution as this will remove all stored data in the fragment.
194
- *
195
- * @example
196
- * ```typescript
197
- * fragment.clear(); // All meta data is now gone
198
- * ```
199
- */
200
- clear(): void {
201
- this._meta.clear();
202
- }
203
-
204
- /**
205
- * Gets the number of items stored in the fragment's meta.
206
- *
207
- * @returns The count of stored meta items
208
- *
209
- * @example
210
- * ```typescript
211
- * console.log(`Fragment contains ${fragment.size()} items`);
212
- * ```
213
- */
214
- size(): number {
215
- return this._meta.size();
216
- }
217
-
218
- /**
219
- * Gets all keys currently stored in the fragment's meta.
220
- *
221
- * @returns Array of all meta keys
222
- *
223
- * @example
224
- * ```typescript
225
- * const keys = fragment.keys();
226
- * console.log('Stored keys:', keys);
227
- * ```
228
- */
229
- keys(): (keyof _MetaItems)[] {
230
- return this._meta.toArray().map(([key]) => key);
231
- }
232
-
233
- /**
234
- * Sets multiple values at once in the fragment's meta.
235
- *
236
- * @param data - Object containing key-value pairs to set
237
- *
238
- * @example
239
- * ```typescript
240
- * fragment.setMultiple({
241
- * userId: '12345',
242
- * role: 'admin',
243
- * lastLogin: new Date()
244
- * });
245
- * ```
246
- */
247
- setMultiple(data: A_TYPES__DeepPartial<_MetaItems>): void {
248
- Object.entries(data).forEach(([key, value]) => {
249
- if (value !== undefined) {
250
- this._meta.set(key as keyof _MetaItems, value);
251
- }
252
- });
253
- }
254
-
255
- /**
256
- * Creates a shallow copy of the fragment with the same meta data.
257
- *
258
- * @param newName - Optional new name for the cloned fragment
259
- * @returns A new fragment instance with copied meta
260
- *
261
- * @example
262
- * ```typescript
263
- * const original = new A_Fragment<{ data: string }>({ name: 'original' });
264
- * original.set('data', 'test');
265
- *
266
- * const clone = original.clone('cloned');
267
- * console.log(clone.get('data')); // 'test'
268
- * ```
269
- */
270
- clone(newName?: string): A_Fragment<_MetaItems, _SerializedType> {
271
- const cloned = new (this.constructor as any)({
272
- name: newName || `${this._name}_copy`
273
- });
274
-
275
- // Copy all meta data
276
- this._meta.toArray().forEach(([key, value]) => {
277
- cloned.set(key, value);
278
- });
279
-
280
- return cloned;
281
- }
282
-
283
-
284
90
  /**
285
91
  * Serializes the fragment to a JSON-compatible object.
286
92
  *
@@ -305,13 +111,8 @@ export class A_Fragment<
305
111
  toJSON(): _SerializedType {
306
112
  const result = {
307
113
  name: this.name,
308
-
309
- ...this.meta.toArray().reduce((acc, [key, value]) => {
310
- acc[key] = value;
311
- return acc;
312
- }, {} as _MetaItems)
313
114
  };
314
115
 
315
- return result as unknown as _SerializedType;
116
+ return result as _SerializedType;
316
117
  }
317
118
  }
@@ -4,8 +4,8 @@
4
4
  * [!] Meta can be different depending on the type of input data
5
5
  */
6
6
  export class A_Meta<
7
- _StorageItems extends Record<string, any> = any
8
- // _StorageItems extends Record<string, Map<string | Symbol, any> | Array<any> | A_TYPES__Dictionary<any>>
7
+ _StorageItems extends Record<string, any> = any,
8
+ _SerializedType extends Record<string, any> = Record<string, any>
9
9
  > implements Iterable<[keyof _StorageItems, _StorageItems[keyof _StorageItems]]> {
10
10
 
11
11
  protected meta: Map<keyof _StorageItems, _StorageItems[keyof _StorageItems]> = new Map();
@@ -192,4 +192,47 @@ export class A_Meta<
192
192
  toArray(): Array<[keyof _StorageItems, _StorageItems[keyof _StorageItems]]> {
193
193
  return Array.from(this.meta.entries());
194
194
  }
195
+
196
+
197
+ protected recursiveToJSON(value: any): any {
198
+ switch (true) {
199
+ case value instanceof A_Meta:
200
+ return value.toJSON();
201
+
202
+ case value instanceof Map:
203
+ const obj: Record<string, any> = {};
204
+ for (const [k, v] of value.entries()) {
205
+ obj[String(k)] = this.recursiveToJSON(v);
206
+ }
207
+ return obj;
208
+
209
+ case Array.isArray(value):
210
+ return value.map((item) => this.recursiveToJSON(item));
211
+
212
+ case !!value && typeof value === 'object':
213
+ const res: Record<string, any> = {};
214
+ for (const [k, v] of Object.entries(value)) {
215
+ res[k] = this.recursiveToJSON(v);
216
+ }
217
+ return res;
218
+
219
+ default:
220
+ return value;
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Serializes the meta to a JSON object
226
+ * Uses internal storage to convert to JSON
227
+ *
228
+ * @returns
229
+ */
230
+ toJSON(): _SerializedType {
231
+ const json: Record<string, any> = {};
232
+
233
+ for (const [key, value] of this.meta.entries()) {
234
+ json[String(key)] = this.recursiveToJSON(value);
235
+ }
236
+ return json as _SerializedType;
237
+ }
195
238
  }
@@ -10,7 +10,6 @@ import {
10
10
  A_TYPES__A_InjectDecorator_EntityInjectionInstructions,
11
11
  A_TYPES__A_InjectDecorator_EntityInjectionQuery,
12
12
  A_TYPES__InjectableConstructors,
13
- A_TYPES__InjectableTargets,
14
13
  } from "@adaas/a-concept/global/A-Inject/A-Inject.types";
15
14
  import { A_Fragment } from "../A-Fragment/A-Fragment.class";
16
15
  import { A_Context } from "../A-Context/A-Context.class";
@@ -27,11 +26,13 @@ import { A_TYPES__Component_Constructor } from '../A-Component/A-Component.types
27
26
  import { A_TYPES__Fragment_Constructor } from '../A-Fragment/A-Fragment.types';
28
27
  import { A_TYPES__Error_Constructor } from '../A-Error/A_Error.types';
29
28
  import { A_TYPES__ComponentMetaKey } from '../A-Component/A-Component.constants';
29
+ import { A_Meta } from '../A-Meta/A-Meta.class';
30
30
 
31
31
 
32
32
 
33
33
 
34
34
  export class A_Scope<
35
+ _MetaItems extends Record<string, any> = any,
35
36
  _ComponentType extends A_TYPES__Component_Constructor[] = A_TYPES__Component_Constructor[],
36
37
  _ErrorType extends A_TYPES__Error_Constructor[] = A_TYPES__Error_Constructor[],
37
38
  _EntityType extends A_TYPES__Entity_Constructor[] = A_TYPES__Entity_Constructor[],
@@ -46,6 +47,12 @@ export class A_Scope<
46
47
  * Parent scope reference, used for inheritance of components, fragments, entities and commands
47
48
  */
48
49
  protected _parent?: A_Scope;
50
+ /**
51
+ * Internal meta storage using A_Meta for type-safe key-value operations.
52
+ * This stores all the scope's runtime data that can be accessed and modified
53
+ * throughout the execution pipeline or within running containers.
54
+ */
55
+ protected _meta: A_Meta<_MetaItems> = new A_Meta<_MetaItems>();
49
56
 
50
57
  // ===========================================================================
51
58
  // --------------------ALLowed Constructors--------------------------------
@@ -100,7 +107,10 @@ export class A_Scope<
100
107
  * Returns the name of the scope
101
108
  */
102
109
  get name() { return this._name }
103
-
110
+ /**
111
+ * Returns the meta object of the scope
112
+ */
113
+ get meta() { return this._meta }
104
114
  /**
105
115
  * Returns a list of Constructors for A-Components that are available in the scope
106
116
  */
@@ -174,14 +184,14 @@ export class A_Scope<
174
184
  /**
175
185
  * A set of constructors that are allowed in the scope
176
186
  */
177
- params: Partial<A_TYPES__Scope_Init<_ComponentType, _ErrorType, _EntityType, _FragmentType>>,
187
+ params: Partial<A_TYPES__Scope_Init<_MetaItems, _ComponentType, _ErrorType, _EntityType, _FragmentType>>,
178
188
  /**
179
189
  * Configuration options for the scope
180
190
  */
181
191
  config?: Partial<A_TYPES__ScopeConfig>
182
192
  )
183
193
  constructor(
184
- param1?: Partial<A_TYPES__Scope_Init<_ComponentType, _ErrorType, _EntityType, _FragmentType>>,
194
+ param1?: Partial<A_TYPES__Scope_Init<_MetaItems, _ComponentType, _ErrorType, _EntityType, _FragmentType>>,
185
195
  param2?: Partial<A_TYPES__ScopeConfig>
186
196
  ) {
187
197
  const initializer = this.getInitializer(param1);
@@ -199,7 +209,7 @@ export class A_Scope<
199
209
  * @returns
200
210
  */
201
211
  protected getInitializer(
202
- param1?: Partial<A_TYPES__Scope_Init<_ComponentType, _ErrorType, _EntityType, _FragmentType>>,
212
+ param1?: Partial<A_TYPES__Scope_Init<_MetaItems, _ComponentType, _ErrorType, _EntityType, _FragmentType>>,
203
213
  param2?: Partial<A_TYPES__ScopeConfig>
204
214
  ): (param1: any, param2: any) => void | (() => void) {
205
215
  switch (true) {
@@ -216,7 +226,7 @@ export class A_Scope<
216
226
 
217
227
 
218
228
  protected defaultInitialized(
219
- params: Partial<A_TYPES__Scope_Init<_ComponentType, _ErrorType, _EntityType, _FragmentType>> = {},
229
+ params: Partial<A_TYPES__Scope_Init<_MetaItems, _ComponentType, _ErrorType, _EntityType, _FragmentType>> = {},
220
230
  config: Partial<A_TYPES__ScopeConfig> = {}
221
231
  ) {
222
232
  this._name = params.name || this.constructor.name
@@ -225,6 +235,7 @@ export class A_Scope<
225
235
  this.initErrors(params.errors);
226
236
  this.initFragments(params.fragments);
227
237
  this.initEntities(params.entities);
238
+ this.initMeta(params.meta);
228
239
 
229
240
  if (config.parent) {
230
241
  this._parent = config.parent;
@@ -272,6 +283,20 @@ export class A_Scope<
272
283
  * @param _fragments
273
284
  */
274
285
  protected initFragments(_fragments?: _FragmentType) { _fragments?.forEach(this.register.bind(this)); }
286
+ /**
287
+ * This method is used to initialize the meta in the scope
288
+ *
289
+ * This method only sets the meta values in the scope in case they are not set yet
290
+ *
291
+ * @param _meta
292
+ */
293
+ protected initMeta(_meta?: Partial<_MetaItems>) {
294
+ if (_meta) {
295
+ Object.entries(_meta).forEach(([key, value]) => {
296
+ this._meta.set(key as keyof _MetaItems, value as _MetaItems[keyof _MetaItems]);
297
+ });
298
+ }
299
+ }
275
300
 
276
301
 
277
302
  // ==========================================================================
@@ -300,6 +325,41 @@ export class A_Scope<
300
325
  }
301
326
 
302
327
 
328
+ /**
329
+ * Retrieves a value from the scope's meta.
330
+ *
331
+ * @param param - The key to retrieve
332
+ * @returns The value associated with the key, or undefined if not found
333
+ *
334
+ * @example
335
+ * ```typescript
336
+ * const userId = scope.get('userId');
337
+ * if (userId) {
338
+ * console.log(`Current user: ${userId}`);
339
+ * }
340
+ * ```
341
+ */
342
+ get<K extends keyof _MetaItems>(param: K): _MetaItems[K] | undefined {
343
+ return this._meta.get(param);
344
+ }
345
+
346
+ /**
347
+ * Stores a value in the scope's meta.
348
+ *
349
+ * @param param - The key to store the value under
350
+ * @param value - The value to store
351
+ *
352
+ * @example
353
+ * ```typescript
354
+ * scope.set('userId', '12345');
355
+ * scope.set('role', 'admin');
356
+ * ```
357
+ */
358
+ set<K extends keyof _MetaItems>(param: K, value: _MetaItems[K]): void {
359
+ this._meta.set(param, value);
360
+ }
361
+
362
+
303
363
  /**
304
364
  * Returns the issuer of the scope, useful for debugging and tracking purposes
305
365
  *
@@ -312,7 +372,7 @@ export class A_Scope<
312
372
  * @returns
313
373
  */
314
374
  issuer<T extends A_TYPES__ScopeLinkedComponents>(): T | undefined {
315
- return A_Context.issuer(this) as T;
375
+ return A_Context.issuer(this) as T;
316
376
  }
317
377
 
318
378
 
@@ -1492,5 +1552,4 @@ export class A_Scope<
1492
1552
 
1493
1553
  console.log(chain.join(' -> '));
1494
1554
  }
1495
- }
1496
-
1555
+ }
@@ -25,6 +25,7 @@ export type A_TYPES__Scope_Constructor<T = A_Scope> = new (...args: any[]) => T;
25
25
  * Scope initialization type
26
26
  */
27
27
  export type A_TYPES__Scope_Init<
28
+ _MetaItems extends Record<string, any> = any,
28
29
  _ComponentType extends A_TYPES__Component_Constructor[] = A_TYPES__Component_Constructor[],
29
30
  _ErrorType extends A_TYPES__Error_Constructor[] = A_TYPES__Error_Constructor[],
30
31
  _EntityType extends A_TYPES__Entity_Constructor[] = A_TYPES__Entity_Constructor[],
@@ -54,6 +55,8 @@ export type A_TYPES__Scope_Init<
54
55
  ..._EntityType,
55
56
  ...InstanceType<_EntityType[number]>[]
56
57
  ];
58
+
59
+ meta: Partial<_MetaItems>;
57
60
  };
58
61
  /**
59
62
  * Scope configuration type
@@ -1,15 +1,48 @@
1
1
  import { A_Container } from '@adaas/a-concept/global/A-Container/A-Container.class';
2
2
  import { A_Scope } from '@adaas/a-concept/global/A-Scope/A-Scope.class';
3
+ import { A_Concept, A_Feature } from '../src';
3
4
 
4
5
 
5
6
  describe('A-Container tests', () => {
6
7
 
7
- it('Should Allow to create a concept', async () => {
8
+ it('Should allow to create a concept', async () => {
8
9
  const container = new A_Container();
9
10
  expect(container).toBeInstanceOf(A_Container);
10
11
  expect(container.scope).toBeDefined();
11
12
  expect(container.scope).toBeInstanceOf(A_Scope);
12
13
  });
14
+ it('Should allow to define and extend features on container level', async () => {
15
+ const results: string[] = [];
16
+
17
+
18
+ class customContainer extends A_Container {
19
+
20
+
21
+ @A_Concept.Start()
22
+ async start() {
23
+ results.push('started');
24
+
25
+ await this.call('testFeature');
26
+ }
27
+
28
+ @A_Feature.Extend({
29
+ name: 'testFeature',
30
+ })
31
+ async extendFeature() {
32
+ results.push('extended');
33
+ }
34
+ }
35
+
36
+
37
+ const myContainer = new customContainer();
38
+ const concept = new A_Concept({
39
+ containers: [myContainer]
40
+ });
41
+
42
+ await concept.start();
43
+
44
+ expect(results).toEqual(['started', 'extended']);
45
+ });
13
46
 
14
47
 
15
48
  });