@adaas/a-concept 0.0.63 → 0.0.64

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.
@@ -2,176 +2,242 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.A_Scope = void 0;
4
4
  const a_utils_1 = require("@adaas/a-utils");
5
+ const A_Component_types_1 = require("../A-Component/A-Component.types");
5
6
  const A_Fragment_class_1 = require("../A-Fragment/A-Fragment.class");
6
7
  const A_Context_class_1 = require("../A-Context/A-Context.class");
7
- const A_Component_types_1 = require("../A-Component/A-Component.types");
8
8
  const A_Component_class_1 = require("../A-Component/A-Component.class");
9
9
  const A_Entity_class_1 = require("../A-Entity/A-Entity.class");
10
10
  const A_Command_class_1 = require("../A-Command/A-Command.class");
11
- /**
12
- *
13
- *
14
- * A_Scope refers to the visibility and accessibility of :
15
- * - variables,
16
- * - Components,
17
- * - Context Fragments
18
- * - and objects in different parts of your code.
19
- * Scope determines where a particular piece of data (like a variable or function)
20
- * can be accessed, modified, or referenced, and it plays a crucial role in avoiding naming collisions and ensuring data integrity.
21
- *
22
- *
23
- */
24
11
  class A_Scope {
12
+ // ===========================================================================
13
+ // --------------------Readonly Allowed Properties----------------------------
14
+ // ===========================================================================
15
+ /**
16
+ * Returns a list of Constructors for A-Components that are available in the scope
17
+ */
18
+ get allowedComponents() { return this._allowedComponents; }
19
+ /**
20
+ * Returns a list of Constructors for A-Commands that are available in the scope
21
+ */
22
+ get allowedCommands() { return this._allowedCommands; }
23
+ /**
24
+ * Returns a list of Constructors for A-Fragments that are available in the scope
25
+ */
26
+ get allowedFragments() { return this._allowedFragments; }
27
+ /**
28
+ * Returns a list of Constructors for A-Entities that are available in the scope
29
+ */
30
+ get allowedEntities() { return this._allowedEntities; }
31
+ /**
32
+ * Returns an Array of entities registered in the scope
33
+ *
34
+ * [!] One entity per aseid
35
+ */
36
+ get entities() { return Array.from(this._entities.values()); }
37
+ /**
38
+ * Returns an Array of fragments registered in the scope
39
+ *
40
+ * [!] One fragment per scope
41
+ */
42
+ get fragments() { return Array.from(this._fragments.values()); }
43
+ /**
44
+ * Returns an Array of components registered in the scope
45
+ *
46
+ * [!] One component instance per scope
47
+ */
48
+ get components() { return Array.from(this._components.values()); }
49
+ /**
50
+ * Returns an Array of commands registered in the scope
51
+ *
52
+ * [!] One command per command aseid
53
+ * [!!] There may be any number of instances of the same command code, but with different aseids.
54
+ */
55
+ get commands() { return Array.from(this._commands.values()); }
56
+ /**
57
+ * A_Scope refers to the visibility and accessibility of :
58
+ * - variables,
59
+ * - Components,
60
+ * - Context Fragments
61
+ * - Commands
62
+ * - Entities
63
+ * - and objects in different parts of your code.
64
+ * Scope determines where a particular piece of data (like a variable or function)
65
+ * can be accessed, modified, or referenced, and it plays a crucial role in avoiding naming collisions and ensuring data integrity.
66
+ *
67
+ * [!] The scope behavior is similar to tree structure where each scope can have a parent scope and inherit its components, fragments, entities and commands
68
+ *
69
+ * @param params
70
+ * @param config
71
+ */
25
72
  constructor(params, config = {}) {
73
+ /**
74
+ * Scope Name uses for identification and logging purposes
75
+ */
26
76
  this.name = '';
27
- this._components = new WeakMap();
28
- this._fragments = new WeakMap();
29
- this._commands = new Map();
77
+ // ===========================================================================
78
+ // --------------------ALLowed Constructors--------------------------------
79
+ // ===========================================================================
80
+ /**
81
+ * A set of allowed components, A set of constructors that are allowed in the scope
82
+ *
83
+ */
84
+ this._allowedComponents = new Set();
85
+ /**
86
+ * A set of allowed entities, A set of constructors that are allowed in the scope
87
+ */
88
+ this._allowedEntities = new Set();
89
+ /**
90
+ * A set of allowed fragments, A set of constructors that are allowed in the scope
91
+ */
92
+ this._allowedFragments = new Set();
93
+ /**
94
+ * A set of allowed commands, A set of constructors that are allowed in the scope
95
+ */
96
+ this._allowedCommands = new Set();
97
+ // ===========================================================================
98
+ // --------------------Internal Storage--------------------------------
99
+ // ===========================================================================
100
+ /**
101
+ * Internal storage for the components, fragments, entities and commands
102
+ */
103
+ this._components = new Map();
104
+ /**
105
+ * Storage for the fragments, should be weak as fragments are singletons per scope
106
+ */
107
+ this._fragments = new Map();
108
+ /**
109
+ * Storage for the entities, should be strong as entities are unique per aseid
110
+ */
30
111
  this._entities = new Map();
112
+ /**
113
+ * Storage for the commands, should be strong as commands are unique per code
114
+ */
115
+ this._commands = new Map();
31
116
  this.name = params.name || this.constructor.name;
32
- // TODO: move to defaults
33
- const defaultParams = {
34
- name: '',
35
- components: [],
36
- fragments: [],
37
- entities: [],
38
- commands: [],
39
- };
40
- this.params = Object.assign(Object.assign({}, defaultParams), params);
41
- this.initComponents(params.components || []);
42
- this.initFragments(params.fragments || []);
43
- this.initEntities(params.entities || []);
117
+ this.initComponents(params.components);
118
+ this.initCommands(params.commands);
119
+ this.initFragments(params.fragments);
120
+ this.initEntities(params.entities);
44
121
  if (config.parent) {
45
122
  this._parent = config.parent;
46
123
  }
47
124
  }
48
- initComponents(_components) {
49
- // _components.forEach(component => {
50
- // this._components.set(component, new component());
51
- // })
52
- }
53
- initEntities(_entities) {
54
- _entities.forEach(this.register.bind(this));
55
- }
56
- initFragments(_fragments) {
57
- _fragments.forEach(this.register.bind(this));
58
- }
59
- get components() {
60
- return this.params.components || [];
61
- }
62
- get commands() {
63
- return this.params.commands || [];
64
- }
65
- get fragments() {
66
- return this.params.fragments || [];
67
- }
125
+ //==========================================================================
126
+ // --------------------Scope Initialization Methods---------------------------
127
+ //==========================================================================
128
+ /**
129
+ * This method is used to initialize the components in the scope
130
+ * To save memory components are initialized only when they are requested
131
+ *
132
+ * This method only registers the component in the scope in case they are not registered yet
133
+ *
134
+ * @param _components
135
+ */
136
+ initComponents(_components) { _components === null || _components === void 0 ? void 0 : _components.forEach(this.register.bind(this)); }
137
+ /**
138
+ * This method is used to initialize the entities in the scope
139
+ *
140
+ * This method only registers the entities in the scope in case they are not registered yet
141
+ *
142
+ * @param _entities
143
+ */
144
+ initEntities(_entities) { _entities === null || _entities === void 0 ? void 0 : _entities.forEach(this.register.bind(this)); }
145
+ /**
146
+ * This method is used to initialize the fragments in the scope
147
+ *
148
+ * This method only registers the fragments in the scope in case they are not registered yet
149
+ *
150
+ * @param _fragments
151
+ */
152
+ initFragments(_fragments) { _fragments === null || _fragments === void 0 ? void 0 : _fragments.forEach(this.register.bind(this)); }
153
+ /**
154
+ * This method is used to initialize the commands in the scope
155
+ *
156
+ * This method only registers the commands in the scope in case they are not registered yet
157
+ *
158
+ * @param _commands
159
+ */
160
+ initCommands(_commands) { _commands === null || _commands === void 0 ? void 0 : _commands.forEach(this.register.bind(this)); }
161
+ /**
162
+ * This method is used to get or set the parent scope
163
+ *
164
+ * [!] Note that setting the parent scope will override the existing parent scope
165
+ *
166
+ * @param setValue
167
+ * @returns
168
+ */
68
169
  parent(setValue) {
69
170
  if (setValue) {
70
171
  return this.inherit(setValue);
71
172
  }
72
173
  return this._parent;
73
174
  }
74
- isInheritedFrom(scope) {
75
- let current = this;
76
- while (current) {
77
- if (current === scope) {
78
- return true;
79
- }
80
- current = current._parent;
81
- }
82
- return false;
83
- }
175
+ /**
176
+ * This method is used to inherit from a parent scope
177
+ *
178
+ * [!] This method checks for circular inheritance and throws an error if detected
179
+ *
180
+ * @param parent
181
+ * @returns
182
+ */
84
183
  inherit(parent) {
85
184
  // Prevent circular inheritance
86
185
  const circularCheck = this.checkCircularInheritance(parent);
87
- if (circularCheck) {
88
- throw new Error(`Circular inheritance detected: ${[...circularCheck, parent.name].join(' -> ')}`);
89
- }
186
+ if (circularCheck)
187
+ throw new a_utils_1.A_Error(`Circular inheritance detected: ${[...circularCheck, parent.name].join(' -> ')}`);
90
188
  this._parent = parent;
91
189
  return this;
92
190
  }
93
- /**
94
- * Helper method to check circular inheritance
95
- * Should return a full sequence of inheritance for logging purposes
96
- *
97
- * @param scope
98
- * @returns
99
- */
100
- checkCircularInheritance(scope) {
101
- const inheritanceChain = [];
102
- let current = this._parent;
103
- while (current) {
104
- inheritanceChain.push(current.name);
105
- if (current === scope) {
106
- return inheritanceChain;
107
- }
108
- current = current._parent;
109
- }
110
- return false;
111
- }
112
- printInheritanceChain() {
113
- const chain = [];
114
- let current = this;
115
- while (current) {
116
- chain.push(current.name);
117
- current = current._parent;
118
- }
119
- console.log(chain.join(' -> '));
120
- }
121
- has(entity) {
191
+ has(ctor) {
192
+ let found = false;
122
193
  switch (true) {
123
- case typeof entity === 'string': {
124
- const possibleComponent = this.params.components.find(c => c.name === entity);
125
- if (possibleComponent) {
126
- return true;
127
- }
128
- const possibleFragment = this.params.fragments.find(f => f.name === entity);
129
- if (possibleFragment) {
130
- return true;
131
- }
132
- if (this.params.entities.some(e => e.constructor.name === entity)) {
133
- return true;
134
- }
194
+ // 1) Check by string name.
195
+ case typeof ctor === 'string': {
196
+ // 1.1 Check if it's a component name
197
+ const possibleComponent = Array.from(this.allowedComponents).find(c => c.name === ctor);
198
+ if (possibleComponent)
199
+ found = true;
200
+ // 1.2 Check if it's a fragment name
201
+ const possibleFragment = Array.from(this.allowedFragments).find(f => f.name === ctor);
202
+ if (possibleFragment)
203
+ found = true;
204
+ // 1.3 Check if it's a command code or name
205
+ const possibleCommand = Array.from(this.allowedCommands).find(c => c.name === ctor);
206
+ if (possibleCommand)
207
+ found = true;
208
+ // 1.4 Check if it's an entity name or entity static entity property
209
+ const possibleEntity = Array.from(this.allowedEntities).find(e => e.name === ctor);
210
+ if (possibleEntity)
211
+ found = true;
212
+ // 1.5 If not found in current scope, check parent scope
135
213
  if (!!this._parent)
136
- return this._parent.has(entity);
214
+ return this._parent.has(ctor);
137
215
  return false;
138
216
  }
139
- case typeof entity === 'function'
140
- && a_utils_1.A_CommonHelper.isInheritedFrom(entity, A_Component_class_1.A_Component):
141
- {
142
- const found = this.components.includes(entity);
143
- if (!found && !!this._parent) {
144
- return this._parent.has(entity);
145
- }
146
- return found;
147
- }
148
- case typeof entity === 'function'
149
- && a_utils_1.A_CommonHelper.isInheritedFrom(entity, A_Entity_class_1.A_Entity):
150
- {
151
- const entities = Array.from(this._entities.values());
152
- const found = entities.find(e => e instanceof entity);
153
- return !!found;
154
- }
155
- case typeof entity === 'function'
156
- && a_utils_1.A_CommonHelper.isInheritedFrom(entity, A_Fragment_class_1.A_Fragment):
157
- {
158
- const found = this._fragments.has(entity);
159
- if (!found && !!this._parent)
160
- return this._parent.has(entity);
161
- return found;
162
- }
163
- case typeof entity === 'function'
164
- && a_utils_1.A_CommonHelper.isInheritedFrom(entity, A_Command_class_1.A_Command):
165
- {
166
- const found = this.commands.includes(entity);
167
- if (!found && !!this._parent)
168
- return this._parent.has(entity);
169
- return found;
170
- }
171
- default: {
172
- return false;
217
+ // 2) Check if it's a Component
218
+ case this.isComponentConstructor(ctor): {
219
+ found = this.isAllowedComponent(ctor);
220
+ break;
221
+ }
222
+ // 3) Check if it's an Entity
223
+ case this.isEntityConstructor(ctor): {
224
+ found = this.isAllowedEntity(ctor);
225
+ break;
226
+ }
227
+ // 4) Check if it's a Fragment
228
+ case this.isFragmentConstructor(ctor): {
229
+ found = this.isAllowedFragment(ctor);
230
+ break;
231
+ }
232
+ // 5) Check if it's a Command
233
+ case this.isCommandConstructor(ctor): {
234
+ found = this.isAllowedCommand(ctor);
235
+ break;
173
236
  }
174
237
  }
238
+ if (!found && !!this._parent)
239
+ return this._parent.has(ctor);
240
+ return found;
175
241
  }
176
242
  /**
177
243
  * Merges two scopes into a new one
@@ -185,52 +251,40 @@ class A_Scope {
185
251
  */
186
252
  merge(anotherScope) {
187
253
  const merged = new A_Scope({
188
- name: `${this.name}&${anotherScope.name}`,
189
- components: Array.from(new Set([
190
- ...this.params.components,
191
- ...anotherScope.params.components
192
- ])),
193
- fragments: Array.from(new Set([
194
- ...this.params.fragments,
195
- ...anotherScope.params.fragments
196
- ])),
197
- entities: Array.from(new Set([
198
- ...this.params.entities,
199
- ...anotherScope.params.entities
200
- ])),
254
+ name: `${this.name} + ${anotherScope.name}`,
255
+ components: [...this.allowedComponents, ...anotherScope.allowedComponents],
256
+ commands: [...this.allowedCommands, ...anotherScope.allowedCommands],
257
+ fragments: [...this.fragments, ...anotherScope.fragments],
258
+ entities: [...this.entities, ...anotherScope.entities],
201
259
  }, {
202
260
  parent: this._parent || anotherScope._parent
203
261
  });
204
262
  return merged;
205
263
  }
206
- /**
207
- * Allows to retrieve the constructor of the component or entity by its name
208
- *
209
- * [!] Notes:
210
- * - In case of search for A-Entity please ensure that provided string corresponds to the static entity property of the class. [!] By default it's the kebab-case of the class name
211
- * - In case of search for A_Command please ensure that provided string corresponds to the static code property of the class. [!] By default it's the kebab-case of the class name
212
- * - In case of search for A_Component please ensure that provided string corresponds to the class name in PascalCase
213
- *
214
- * @param name
215
- * @returns
216
- */
217
264
  resolveConstructor(name) {
218
- // Check components
219
- const component = this.params.components.find(c => c.name === a_utils_1.A_CommonHelper.toPascalCase(name));
265
+ // 1) Check components
266
+ const component = Array.from(this.allowedComponents).find(c => c.name === name
267
+ || c.name === a_utils_1.A_CommonHelper.toPascalCase(name));
220
268
  if (component)
221
269
  return component;
222
- // Check entities
223
- const entity = this.params.entities.find(e => e.constructor.entity === name
224
- || e.constructor.name === a_utils_1.A_CommonHelper.toPascalCase(name)
225
- || e.constructor.entity === a_utils_1.A_CommonHelper.toKebabCase(name));
270
+ // 2) Check entities
271
+ const entity = Array.from(this.allowedEntities).find(e => e.name === name
272
+ || e.name === a_utils_1.A_CommonHelper.toPascalCase(name)
273
+ || e.entity === name
274
+ || e.entity === a_utils_1.A_CommonHelper.toKebabCase(name));
226
275
  if (entity)
227
- return entity.constructor;
228
- // Check commands
229
- const command = this.params.commands.find(c => c.code === name
276
+ return entity;
277
+ // 3) Check commands
278
+ const command = Array.from(this.allowedCommands).find(c => c.code === name
230
279
  || c.name === a_utils_1.A_CommonHelper.toPascalCase(name)
231
280
  || c.code === a_utils_1.A_CommonHelper.toKebabCase(name));
232
281
  if (command)
233
282
  return command;
283
+ // 4) Check fragments
284
+ const fragment = Array.from(this.allowedFragments).find(f => f.name === name
285
+ || f.name === a_utils_1.A_CommonHelper.toPascalCase(name));
286
+ if (fragment)
287
+ return fragment;
234
288
  // If not found in current scope, check parent scope
235
289
  if (!!this._parent) {
236
290
  return this._parent.resolveConstructor(name);
@@ -238,10 +292,14 @@ class A_Scope {
238
292
  throw new Error(`Component or Entity with name ${name} not found in the scope ${this.name}`);
239
293
  }
240
294
  // base definition
241
- resolve(param1, param2) {
295
+ resolve(
296
+ /**
297
+ * Provide a component, fragment or entity constructor or an array of constructors to resolve its instance(s) from the scope
298
+ */
299
+ param1, param2) {
242
300
  switch (true) {
243
301
  case Array.isArray(param1): {
244
- return param1.map(c => this.resolveOnce(param1, param2));
302
+ return param1.map(c => this.resolveOnce(c, param2)).filter(Boolean);
245
303
  }
246
304
  case typeof param1 === 'function': {
247
305
  return this.resolveOnce(param1, param2);
@@ -250,59 +308,99 @@ class A_Scope {
250
308
  return this.resolveByName(param1);
251
309
  }
252
310
  default: {
253
- throw new Error('Invalid arguments provided');
311
+ throw new a_utils_1.A_Error(`Invalid parameter provided to resolve method: ${param1} in scope ${this.name}`);
254
312
  }
255
313
  }
256
314
  }
315
+ // ==================================================================================================
316
+ // --------------------------------------------------------------------------------------------------
317
+ // -------------------------------------INTERNAL RESOLVERS-------------------------------------------
318
+ // --------------------------------------------------------------------------------------------------
319
+ // ==================================================================================================
257
320
  resolveByName(name) {
258
- // Check components
259
- const component = this.params.components.find(c => c.name === name);
321
+ // 1) Check components
322
+ const component = Array.from(this.allowedComponents).find(c => c.name === name
323
+ || c.name === a_utils_1.A_CommonHelper.toPascalCase(name));
260
324
  if (component)
261
- return this.resolveComponent(component);
262
- // Check commands
263
- const command = this.params.commands.find(c => c.name === name);
325
+ return this.resolveOnce(component);
326
+ // 2) Check entities
327
+ const entity = Array.from(this.allowedEntities).find(e => e.name === name
328
+ || e.name === a_utils_1.A_CommonHelper.toPascalCase(name)
329
+ || e.entity === name
330
+ || e.entity === a_utils_1.A_CommonHelper.toKebabCase(name));
331
+ if (entity)
332
+ return this.resolveOnce(entity);
333
+ // 3) Check commands
334
+ const command = Array.from(this.allowedCommands).find(c => c.code === name
335
+ || c.name === a_utils_1.A_CommonHelper.toPascalCase(name)
336
+ || c.code === a_utils_1.A_CommonHelper.toKebabCase(name));
264
337
  if (command)
265
- return this.resolveComponent(command);
266
- // Check fragments
267
- const fragment = this.params.fragments.find(f => f.constructor.name === name);
338
+ return this.resolveOnce(command);
339
+ // 4) Check fragments
340
+ const fragment = Array.from(this.allowedFragments).find(f => f.name === name
341
+ || f.name === a_utils_1.A_CommonHelper.toPascalCase(name));
268
342
  if (fragment)
269
- return this.resolveFragment(fragment.constructor);
270
- // Check entities
271
- const entity = this.params.entities.find(e => e.constructor.name === name);
272
- if (entity)
273
- return this.resolveEntity(entity.constructor);
343
+ return this.resolveOnce(fragment);
274
344
  // If not found in current scope, check parent scope
275
- if (this._parent) {
345
+ if (!!this._parent) {
276
346
  return this._parent.resolveByName(name);
277
347
  }
278
- throw new Error(`Component, Fragment, or Entity with name ${name} not found in the scope ${this.name}`);
348
+ throw new Error(`Component or Entity with name ${name} not found in the scope ${this.name}`);
279
349
  }
350
+ /**
351
+ * This method is used internally to resolve a single component, fragment or entity from the scope
352
+ *
353
+ * @param component
354
+ * @param instructions
355
+ * @returns
356
+ */
280
357
  resolveOnce(component, instructions) {
358
+ if (this.isScopeConstructor(component))
359
+ component;
360
+ if (typeof component == 'function' && component.name === 'A_Scope')
361
+ component;
281
362
  switch (true) {
282
- case a_utils_1.A_CommonHelper.isInheritedFrom(component, A_Entity_class_1.A_Entity): {
363
+ case this.isEntityConstructor(component): {
283
364
  return this.resolveEntity(component, instructions);
284
365
  }
285
- case a_utils_1.A_CommonHelper.isInheritedFrom(component, A_Fragment_class_1.A_Fragment): {
366
+ case this.isFragmentConstructor(component): {
286
367
  return this.resolveFragment(component);
287
368
  }
288
- case a_utils_1.A_CommonHelper.isInheritedFrom(component, A_Scope): {
369
+ case this.isCommandConstructor(component): {
370
+ return this.resolveCommand(component);
371
+ }
372
+ case this.isScopeConstructor(component): {
289
373
  return this.resolveScope(component);
290
374
  }
291
- case a_utils_1.A_CommonHelper.isInheritedFrom(component, A_Component_class_1.A_Component): {
375
+ case this.isComponentConstructor(component): {
292
376
  return this.resolveComponent(component);
293
377
  }
294
378
  default:
295
379
  throw new Error(`Injected Component ${component} not found in the scope`);
296
380
  }
297
381
  }
382
+ /**
383
+ * This method is used internally to resolve a single entity from the scope based on the provided instructions
384
+ *
385
+ * [!] Note that this method can return either a single entity or an array of entities depending on the instructions provided
386
+ *
387
+ * @param entity
388
+ * @param instructions
389
+ * @returns
390
+ */
298
391
  resolveEntity(entity, instructions) {
299
392
  var _a;
300
393
  const query = (instructions === null || instructions === void 0 ? void 0 : instructions.query) || {};
301
394
  const count = ((_a = instructions === null || instructions === void 0 ? void 0 : instructions.pagination) === null || _a === void 0 ? void 0 : _a.count) || 1;
302
395
  switch (true) {
396
+ /**
397
+ * 1) In case when no instructions provided, return the first found entity of the provided type
398
+ *
399
+ * [!] Note that it returns ONLY ONE entity
400
+ * [!!] In case when no entity found in the current scope, it tries to resolve it from the parent scope (if exists)
401
+ */
303
402
  case !instructions: {
304
- const entities = Array.from(this._entities.values());
305
- const found = entities.find(e => e instanceof entity);
403
+ const found = this.entities.find(e => e instanceof entity);
306
404
  switch (true) {
307
405
  case !!found:
308
406
  return found;
@@ -312,12 +410,22 @@ class A_Scope {
312
410
  throw new Error(`Entity ${entity.name} not found in the scope ${this.name}`);
313
411
  }
314
412
  }
413
+ /**
414
+ * 2) In case when aseid is provided in the query, we can directly get the entity from the map
415
+ *
416
+ * [!] Note that it returns ONLY ONE entity
417
+ */
315
418
  case !!query.aseid
316
419
  && typeof query.aseid === 'string'
317
420
  && this._entities.has(query.aseid):
318
421
  {
319
422
  return this._entities.get(query.aseid);
320
423
  }
424
+ /**
425
+ * 3) In case when aseid is provided as ASEID instance, we can directly get the entity from the map
426
+ *
427
+ * [!] Note that it returns ONLY ONE entity
428
+ */
321
429
  case !!query.aseid
322
430
  && typeof query.aseid === 'object'
323
431
  && query.aseid instanceof a_utils_1.ASEID
@@ -325,18 +433,30 @@ class A_Scope {
325
433
  {
326
434
  return this._entities.get(query.aseid.toString());
327
435
  }
436
+ /**
437
+ * 4) In case when id is provided in the query, we have to find the entity by the id
438
+ *
439
+ * [!] Note that it returns ONLY ONE entity
440
+ */
328
441
  case !!query.id: {
329
- // in this case we have to find the entity by the id
330
- const entities = Array.from(this._entities.values());
331
- const found = entities.filter(e => e instanceof entity).find(e => {
332
- return String(e.id) === String(query.id);
333
- });
442
+ const found = this.entities
443
+ .filter(e => e instanceof entity)
444
+ .find(e => String(e.id) === String(query.id));
334
445
  return found;
335
446
  }
447
+ /**
448
+ * 5) In case when there's a query object, we have to filter the entities by the query
449
+ *
450
+ * [!] Note that it can return either a single entity or an array of entities depending on the count instruction
451
+ * [!!] In case when no entity found in the current scope, it tries to resolve it from the parent scope (if exists)
452
+ */
336
453
  default: {
337
- const entities = Array.from(this._entities.values());
338
- const found = entities.filter(e => e instanceof entity).filter(e => {
339
- return Object.entries(query).every(([key, value]) => {
454
+ const found = this.entities
455
+ .filter(e => e instanceof entity)
456
+ .filter(e => {
457
+ return Object
458
+ .entries(query)
459
+ .every(([key, value]) => {
340
460
  if (key in e) {
341
461
  return e[key] === value;
342
462
  }
@@ -351,33 +471,49 @@ class A_Scope {
351
471
  }
352
472
  }
353
473
  }
474
+ /**
475
+ * This method is used internally to resolve a single fragment from the scope
476
+ *
477
+ * @param fragment
478
+ * @returns
479
+ */
354
480
  resolveFragment(fragment) {
355
- const fragmentInstancePresented = this.fragments.some(fr => fr instanceof fragment);
481
+ const fragmentInstancePresented = this._fragments.get(fragment);
356
482
  switch (true) {
357
483
  case fragmentInstancePresented && this._fragments.has(fragment):
358
- return this._fragments.get(fragment);
359
- case fragmentInstancePresented && !this._fragments.has(fragment):
360
- return this.fragments.find(fr => fr instanceof fragment);
484
+ return fragmentInstancePresented;
361
485
  case !fragmentInstancePresented && !!this._parent:
362
486
  return this._parent.resolveFragment(fragment);
363
487
  default:
364
488
  throw new Error(`Fragment ${fragment.name} not found in the scope ${this.name}`);
365
489
  }
366
490
  }
491
+ /**
492
+ * This method is used internally to resolve a single scope from the current scope
493
+ *
494
+ * @param scope
495
+ * @returns
496
+ */
367
497
  resolveScope(scope) {
368
498
  return this;
369
499
  }
500
+ /**
501
+ * This method is used internally to resolve a single component from the scope
502
+ *
503
+ * @param component
504
+ * @returns
505
+ */
370
506
  resolveComponent(component) {
371
507
  // The idea here that in case when Scope has no exact component we have to resolve it from the _parent
372
508
  // BUT: if it's not presented in _parent we have to check for inheritance
373
509
  // That means that we should ensure that there's no components that are children of the required component
374
510
  switch (true) {
375
- // In case when the component is available and exists in the scope
376
- case this.components.includes(component) && this._components.has(component): {
511
+ // 1) In case when the component is available and exists in the scope
512
+ case this.allowedComponents.has(component) && this._components.has(component): {
377
513
  return this._components.get(component);
378
514
  }
379
- // In case the component available but does NOT exist in the scope
380
- case this.components.includes(component) && !this._components.has(component): {
515
+ // 2) In case the component available but does NOT exist in the scope
516
+ case this.allowedComponents.has(component) && !this._components.has(component): {
381
517
  const componentMeta = A_Context_class_1.A_Context.meta(component);
382
518
  const argsMeta = componentMeta.get(A_Component_types_1.A_TYPES__ComponentMetaKey.INJECTIONS);
383
519
  const resolvedArgs = ((argsMeta === null || argsMeta === void 0 ? void 0 : argsMeta.get('constructor')) || [])
@@ -386,19 +522,20 @@ class A_Scope {
386
522
  const { target, instructions } = arg;
387
523
  return this.resolve(target, instructions);
388
524
  }
525
+ // TODO: Fix types mismatch here
389
526
  return this.resolve(arg.target);
390
527
  });
391
528
  const newComponent = new component(...resolvedArgs);
392
529
  this.register(newComponent);
393
530
  return this._components.get(component);
394
531
  }
395
- // In case when there's a component that is inherited from the required component
396
- case !this.components.includes(component) && this.components.some(el => a_utils_1.A_CommonHelper.isInheritedFrom(el, component)): {
397
- const found = this.components.find(el => a_utils_1.A_CommonHelper.isInheritedFrom(el, component));
532
+ // 3) In case when there's a component that is inherited from the required component
533
+ case !this.allowedComponents.has(component) && Array.from(this.allowedComponents).some(el => a_utils_1.A_CommonHelper.isInheritedFrom(el, component)): {
534
+ const found = Array.from(this.allowedComponents).find(el => a_utils_1.A_CommonHelper.isInheritedFrom(el, component));
398
535
  return this.resolveComponent(found);
399
536
  }
400
- // In case when the component is not available in the scope but the _parent is available
401
- case !this.components.includes(component) && !!this._parent: {
537
+ // 4) In case when the component is not available in the scope but the _parent is available
538
+ case !!this._parent: {
402
539
  return this._parent.resolveComponent(component);
403
540
  }
404
541
  default:
@@ -411,8 +548,7 @@ class A_Scope {
411
548
  * @param command
412
549
  */
413
550
  resolveCommand(command) {
414
- const commands = Array.from(this._commands.values());
415
- const found = commands.find(e => e instanceof command);
551
+ const found = this.commands.find(e => e instanceof command);
416
552
  switch (true) {
417
553
  case !!found:
418
554
  return found;
@@ -424,56 +560,66 @@ class A_Scope {
424
560
  }
425
561
  register(param1) {
426
562
  switch (true) {
427
- case param1 instanceof A_Component_class_1.A_Component && !this._components.has(param1.constructor): {
563
+ // ------------------------------------------
564
+ // ------------ Instances ----------------
565
+ // ------------------------------------------
566
+ // 1) In case when it's a A-Component instance
567
+ case param1 instanceof A_Component_class_1.A_Component: {
568
+ if (!this.allowedComponents.has(param1.constructor))
569
+ this.allowedComponents.add(param1.constructor);
428
570
  this._components.set(param1.constructor, param1);
429
- const allowedComponent = this.components.find(c => c === param1.constructor);
430
- if (!allowedComponent) {
431
- this.components.push(param1.constructor);
432
- }
433
571
  A_Context_class_1.A_Context.register(this, param1);
434
572
  break;
435
573
  }
574
+ // 2) In case when it's a A-Command instance
575
+ case param1 instanceof A_Command_class_1.A_Command: {
576
+ if (!this.allowedCommands.has(param1.constructor))
577
+ this.allowedCommands.add(param1.constructor);
578
+ this._commands.set(param1.constructor.code, param1);
579
+ A_Context_class_1.A_Context.register(this, param1);
580
+ break;
581
+ }
582
+ // 3) In case when it's a A-Entity instance
436
583
  case param1 instanceof A_Entity_class_1.A_Entity && !this._entities.has(param1.aseid.toString()): {
584
+ if (!this.allowedEntities.has(param1.constructor))
585
+ this.allowedEntities.add(param1.constructor);
437
586
  this._entities.set(param1.aseid.toString(), param1);
438
587
  A_Context_class_1.A_Context.register(this, param1);
439
588
  break;
440
589
  }
441
- case param1 instanceof A_Fragment_class_1.A_Fragment && !this._fragments.has(param1.constructor): {
442
- const allowedFragment = this.fragments.find(fr => fr instanceof param1.constructor);
443
- if (!allowedFragment) {
444
- this.fragments.push(param1);
445
- }
590
+ // 4) In case when it's a A-Fragment instance
591
+ case param1 instanceof A_Fragment_class_1.A_Fragment: {
592
+ if (!this.allowedFragments.has(param1.constructor))
593
+ this.allowedFragments.add(param1.constructor);
446
594
  this._fragments.set(param1.constructor, param1);
447
595
  A_Context_class_1.A_Context.register(this, param1);
448
596
  break;
449
597
  }
450
- case param1 instanceof A_Component_class_1.A_Component: {
451
- this._components.set(param1.constructor, param1);
452
- const allowedComponent = this.components.find(c => c === param1.constructor);
453
- if (!allowedComponent) {
454
- this.components.push(param1.constructor);
455
- }
456
- A_Context_class_1.A_Context.register(this, param1);
598
+ // ------------------------------------------
599
+ // ------------ Constructors ----------------
600
+ // ------------------------------------------
601
+ // 5) In case when it's a A-Component constructor
602
+ case this.isComponentConstructor(param1): {
603
+ if (!this.allowedComponents.has(param1))
604
+ this.allowedComponents.add(param1);
457
605
  break;
458
606
  }
459
- case typeof param1 === 'function' && a_utils_1.A_CommonHelper.isInheritedFrom(param1, A_Component_class_1.A_Component): {
460
- const allowedComponent = this.components.find(c => c === param1);
461
- if (!allowedComponent)
462
- this.components.push(param1);
607
+ // 6) In case when it's a A-Command constructor
608
+ case this.isCommandConstructor(param1): {
609
+ if (!this.allowedCommands.has(param1))
610
+ this.allowedCommands.add(param1);
463
611
  break;
464
612
  }
465
- case typeof param1 === 'function' && a_utils_1.A_CommonHelper.isInheritedFrom(param1, A_Entity_class_1.A_Entity): {
466
- const allowedEntity = this.params.entities.find(e => e.constructor === param1);
467
- if (!allowedEntity) {
468
- this.params.entities.push(new param1());
469
- }
613
+ // 7) In case when it's a A-Fragment constructor
614
+ case this.isFragmentConstructor(param1): {
615
+ if (!this.allowedFragments.has(param1))
616
+ this.allowedFragments.add(param1);
470
617
  break;
471
618
  }
472
- case typeof param1 === 'function' && a_utils_1.A_CommonHelper.isInheritedFrom(param1, A_Command_class_1.A_Command): {
473
- const allowedCommand = this.commands.find(c => c === param1);
474
- if (!allowedCommand) {
475
- this.commands.push(param1);
476
- }
619
+ // 8) In case when it's a A-Entity constructor
620
+ case this.isEntityConstructor(param1): {
621
+ if (!this.allowedEntities.has(param1))
622
+ this.allowedEntities.add(param1);
477
623
  break;
478
624
  }
479
625
  default:
@@ -485,12 +631,158 @@ class A_Scope {
485
631
  throw new Error(`Cannot register ${param1} in the scope ${this.name}`);
486
632
  }
487
633
  }
634
+ /**
635
+ * This method is useful when you want to serialize the scope to JSON
636
+ *
637
+ * [!] Note this is not a deep serialization, only the fragments are serialized
638
+ * [!] Fragments are a storage for information which is relevant to the scope
639
+ *
640
+ * @returns
641
+ */
488
642
  toJSON() {
489
- return this.fragments.reduce((acc, fragment) => {
643
+ return this.fragments
644
+ .reduce((acc, fragment) => {
490
645
  const serialized = fragment.toJSON();
491
646
  return Object.assign(Object.assign({}, acc), { [serialized.name]: serialized });
492
647
  }, {});
493
648
  }
649
+ //==========================================================================
650
+ // --------------------Scope Type Check Helpers---------------------------
651
+ //==========================================================================
652
+ /**
653
+ * Type guard to check if the constructor is of type A_Component
654
+ *
655
+ * @param ctor
656
+ * @returns
657
+ */
658
+ isComponentConstructor(ctor) {
659
+ return typeof ctor === 'function' && a_utils_1.A_CommonHelper.isInheritedFrom(ctor, A_Component_class_1.A_Component);
660
+ }
661
+ /**
662
+ * Type guard to check if the constructor is of type A_Command
663
+ *
664
+ * @param ctor
665
+ * @returns
666
+ */
667
+ isCommandConstructor(ctor) {
668
+ return typeof ctor === 'function' && a_utils_1.A_CommonHelper.isInheritedFrom(ctor, A_Command_class_1.A_Command);
669
+ }
670
+ /**
671
+ * Type guard to check if the constructor is of type A_Fragment
672
+ *
673
+ * @param ctor
674
+ * @returns
675
+ */
676
+ isFragmentConstructor(ctor) {
677
+ return typeof ctor === 'function' && a_utils_1.A_CommonHelper.isInheritedFrom(ctor, A_Fragment_class_1.A_Fragment);
678
+ }
679
+ /**
680
+ * Type guard to check if the constructor is of type A_Entity
681
+ *
682
+ * @param ctor
683
+ * @returns
684
+ */
685
+ isEntityConstructor(ctor) {
686
+ return typeof ctor === 'function' && a_utils_1.A_CommonHelper.isInheritedFrom(ctor, A_Entity_class_1.A_Entity);
687
+ }
688
+ /**
689
+ * Type guard to check if the constructor is of type A_Scope
690
+ *
691
+ * @param ctor
692
+ * @returns
693
+ */
694
+ isScopeConstructor(ctor) {
695
+ return typeof ctor === 'function' && a_utils_1.A_CommonHelper.isInheritedFrom(ctor, A_Scope);
696
+ }
697
+ // -------------------------------------------------------------------------------
698
+ // --------------------Scope Allowed Type Check Helpers---------------------------
699
+ // -------------------------------------------------------------------------------
700
+ /**
701
+ * Type guard to check if the constructor is of type A_Component and is allowed in the scope
702
+ *
703
+ * @param ctor
704
+ * @returns
705
+ */
706
+ isAllowedComponent(ctor) {
707
+ return this.isComponentConstructor(ctor) && this.allowedComponents.has(ctor);
708
+ }
709
+ /**
710
+ * Type guard to check if the constructor is of type A_Command and is allowed in the scope
711
+ *
712
+ * @param ctor
713
+ * @returns
714
+ */
715
+ isAllowedCommand(ctor) {
716
+ return this.isCommandConstructor(ctor) && this.allowedCommands.has(ctor);
717
+ }
718
+ /**
719
+ * Type guard to check if the constructor is of type A_Entity and is allowed in the scope
720
+ *
721
+ * @param ctor
722
+ * @returns
723
+ */
724
+ isAllowedEntity(ctor) {
725
+ return this.isEntityConstructor(ctor) && this.allowedEntities.has(ctor);
726
+ }
727
+ /**
728
+ * Type guard to check if the constructor is of type A_Fragment and is allowed in the scope
729
+ *
730
+ * @param ctor
731
+ * @returns
732
+ */
733
+ isAllowedFragment(ctor) {
734
+ return this.isFragmentConstructor(ctor) && this.allowedFragments.has(ctor);
735
+ }
736
+ // ==========================================================================
737
+ // --------------------DEBUG & Helpers Methods--------------------------------
738
+ // ===========================================================================
739
+ /**
740
+ * This method is used to check if the scope is inherited from another scope
741
+ *
742
+ * @param scope
743
+ * @returns
744
+ */
745
+ isInheritedFrom(scope) {
746
+ let current = this;
747
+ while (current) {
748
+ if (current === scope) {
749
+ return true;
750
+ }
751
+ current = current._parent;
752
+ }
753
+ return false;
754
+ }
755
+ /**
756
+ * Helper method to check circular inheritance
757
+ * Should return a full sequence of inheritance for logging purposes
758
+ *
759
+ * @param scope
760
+ * @returns
761
+ */
762
+ checkCircularInheritance(scope) {
763
+ const inheritanceChain = [];
764
+ let current = this._parent;
765
+ while (current) {
766
+ inheritanceChain.push(current.name);
767
+ if (current === scope) {
768
+ return inheritanceChain;
769
+ }
770
+ current = current._parent;
771
+ }
772
+ return false;
773
+ }
774
+ /**
775
+ * Helper method to print the inheritance chain of the scope
776
+ */
777
+ printInheritanceChain() {
778
+ const chain = [];
779
+ let current = this;
780
+ while (current) {
781
+ chain.push(current.name);
782
+ current = current._parent;
783
+ }
784
+ console.log(chain.join(' -> '));
785
+ }
494
786
  }
495
787
  exports.A_Scope = A_Scope;
496
788
  //# sourceMappingURL=A-Scope.class.js.map