@adaas/a-utils 0.1.12 → 0.1.14

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/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { A_Feature, A_Inject, A_Scope, A_Concept, A_Container, A_Error, A_Component, A_Fragment, A_CONSTANTS__DEFAULT_ENV_VARIABLES_ARRAY, A_CommonHelper, A_FormatterHelper, A_Context, A_Entity, A_ScopeError, A_TypeGuards } from '@adaas/a-concept';
1
+ import { A_Feature, A_Inject, A_Scope, A_Concept, A_Container, A_Error, A_TypeGuards, A_Component, A_Fragment, A_CONSTANTS__DEFAULT_ENV_VARIABLES_ARRAY, A_CommonHelper, A_FormatterHelper, A_Context, A_IdentityHelper, A_Entity, A_ScopeError } from '@adaas/a-concept';
2
2
 
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -11,27 +11,149 @@ var __decorateClass = (decorators, target, key, kind) => {
11
11
  return result;
12
12
  };
13
13
  var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
14
+ var A_ChannelRequestContext = class extends A_Fragment {
15
+ constructor(params = {}) {
16
+ super();
17
+ this._errors = /* @__PURE__ */ new Set();
18
+ this._status = "PENDING" /* PENDING */;
19
+ this._params = params;
20
+ }
21
+ /**
22
+ * Returns the status of the request
23
+ */
24
+ get status() {
25
+ return this._status;
26
+ }
27
+ /**
28
+ * Returns the parameters of the request
29
+ */
30
+ get failed() {
31
+ return this._errors.size > 0;
32
+ }
33
+ /**
34
+ * Returns the Params of the Request
35
+ */
36
+ get params() {
37
+ return this._params;
38
+ }
39
+ /**
40
+ * Returns the Result of the Request
41
+ */
42
+ get data() {
43
+ return this._result;
44
+ }
45
+ get errors() {
46
+ return this._errors.size > 0 ? this._errors : void 0;
47
+ }
48
+ // ==========================================================
49
+ // ==================== Mutations ===========================
50
+ // ==========================================================
51
+ /**
52
+ * Adds an error to the context
53
+ *
54
+ * @param error
55
+ */
56
+ fail(error) {
57
+ this._status = "FAILED" /* FAILED */;
58
+ this._errors.add(error);
59
+ }
60
+ /**
61
+ * Sets the result of the request
62
+ *
63
+ * @param result
64
+ */
65
+ succeed(result) {
66
+ this._status = "SUCCESS" /* SUCCESS */;
67
+ this._result = result;
68
+ }
69
+ /**
70
+ * Serializes the context to a JSON object
71
+ *
72
+ * @returns
73
+ */
74
+ toJSON() {
75
+ return {
76
+ params: this._params,
77
+ result: this._result,
78
+ status: this._status,
79
+ errors: this.errors ? Array.from(this._errors).map((err) => err.toString()) : void 0
80
+ };
81
+ }
82
+ };
83
+
84
+ // src/lib/A-Channel/A-Channel.error.ts
14
85
  var A_ChannelError = class extends A_Error {
86
+ /**
87
+ * Channel Error allows to keep track of errors within a channel if something goes wrong
88
+ *
89
+ *
90
+ * @param originalError
91
+ * @param context
92
+ */
93
+ constructor(originalError, context) {
94
+ if (A_TypeGuards.isString(context))
95
+ super(originalError, context);
96
+ else
97
+ super(originalError);
98
+ if (context instanceof A_ChannelRequestContext)
99
+ this._context = context;
100
+ }
101
+ /***
102
+ * Returns Context of the error
103
+ */
104
+ get context() {
105
+ return this._context;
106
+ }
15
107
  };
108
+ // ==========================================================
109
+ // ==================== Error Types =========================
110
+ // ==========================================================
16
111
  A_ChannelError.MethodNotImplemented = "A-Channel Method Not Implemented";
17
112
 
18
113
  // src/lib/A-Channel/A-Channel.component.ts
19
114
  var A_Channel = class extends A_Component {
115
+ /**
116
+ * Creates a new A_Channel instance.
117
+ *
118
+ * The channel must be registered with A_Context before use:
119
+ * ```typescript
120
+ * const channel = new A_Channel();
121
+ * A_Context.root.register(channel);
122
+ * ```
123
+ */
20
124
  constructor() {
21
- super(...arguments);
125
+ super();
22
126
  /**
23
- * Indicates whether the channel is processing requests
127
+ * Indicates whether the channel is currently processing requests.
128
+ * This flag is managed automatically during request/send operations.
129
+ *
130
+ * @readonly
24
131
  */
25
132
  this._processing = false;
133
+ /**
134
+ * Internal cache storage for channel-specific data.
135
+ * Can be used by custom implementations for caching responses,
136
+ * connection pools, or other channel-specific state.
137
+ *
138
+ * @protected
139
+ */
140
+ this._cache = /* @__PURE__ */ new Map();
26
141
  }
27
142
  /**
28
- * Indicates whether the channel is processing requests
29
- */
143
+ * Indicates whether the channel is currently processing requests.
144
+ *
145
+ * @returns {boolean} True if channel is processing, false otherwise
146
+ */
30
147
  get processing() {
31
148
  return this._processing;
32
149
  }
33
150
  /**
34
- * Indicates whether the channel is connected
151
+ * Promise that resolves when the channel is fully initialized.
152
+ *
153
+ * Automatically calls the onConnect lifecycle hook if not already called.
154
+ * This ensures the channel is ready for communication operations.
155
+ *
156
+ * @returns {Promise<void>} Promise that resolves when initialization is complete
35
157
  */
36
158
  get initialize() {
37
159
  if (!this._initialized) {
@@ -39,48 +161,228 @@ var A_Channel = class extends A_Component {
39
161
  }
40
162
  return this._initialized;
41
163
  }
164
+ async onConnect(...args) {
165
+ }
166
+ async onDisconnect(...args) {
167
+ }
168
+ async onBeforeRequest(...args) {
169
+ }
170
+ async onRequest(...args) {
171
+ }
172
+ async onAfterRequest(...args) {
173
+ }
174
+ async onError(...args) {
175
+ }
176
+ async onSend(...args) {
177
+ }
178
+ // ==========================================================
179
+ // ================= Public API Methods ===================
180
+ // ==========================================================
42
181
  /**
43
- * Initializes the channel before use
182
+ * Initializes the channel by calling the onConnect lifecycle hook.
183
+ *
184
+ * This method is called automatically when accessing the `initialize` property.
185
+ * You can also call it manually if needed.
186
+ *
187
+ * @returns {Promise<void>} Promise that resolves when connection is established
44
188
  */
45
189
  async connect() {
46
- throw new A_ChannelError(
47
- A_ChannelError.MethodNotImplemented,
48
- `The connect method is not implemented in ${this.constructor.name} channel. This method is required to initialize the channel before use. So please implement it in the derived class.`
49
- );
190
+ await this.call("onConnect" /* onConnect */);
50
191
  }
51
192
  /**
52
- * Allows to send a request through the channel
53
- *
54
- * @param req - The request parameters
55
- * @returns The response from the channel
193
+ * Disconnects the channel by calling the onDisconnect lifecycle hook.
194
+ *
195
+ * Use this method to properly cleanup resources when the channel is no longer needed.
196
+ *
197
+ * @returns {Promise<void>} Promise that resolves when cleanup is complete
198
+ */
199
+ async disconnect() {
200
+ await this.call("onDisconnect" /* onDisconnect */);
201
+ }
202
+ /**
203
+ * Sends a request and waits for a response (Request/Response pattern).
204
+ *
205
+ * This method follows the complete request lifecycle:
206
+ * 1. Ensures channel is initialized
207
+ * 2. Creates request scope and context
208
+ * 3. Calls onBeforeRequest hook
209
+ * 4. Calls onRequest hook (main processing)
210
+ * 5. Calls onAfterRequest hook
211
+ * 6. Returns the response context
212
+ *
213
+ * If any step fails, the onError hook is called and the error is captured
214
+ * in the returned context.
215
+ *
216
+ * @template _ParamsType The type of request parameters
217
+ * @template _ResultType The type of response data
218
+ * @param params The request parameters
219
+ * @returns {Promise<A_ChannelRequestContext<_ParamsType, _ResultType>>} Request context with response
220
+ *
221
+ * @example
222
+ * ```typescript
223
+ * // Basic usage
224
+ * const response = await channel.request({ action: 'getData', id: 123 });
225
+ *
226
+ * // Typed usage
227
+ * interface UserRequest { userId: string; }
228
+ * interface UserResponse { name: string; email: string; }
229
+ *
230
+ * const userResponse = await channel.request<UserRequest, UserResponse>({
231
+ * userId: 'user-123'
232
+ * });
233
+ *
234
+ * if (!userResponse.failed) {
235
+ * console.log('User:', userResponse.data.name);
236
+ * }
237
+ * ```
56
238
  */
57
239
  async request(params) {
58
- throw new A_ChannelError(
59
- A_ChannelError.MethodNotImplemented,
60
- `The request method is not implemented in ${this.constructor.name} channel.`
61
- );
240
+ await this.initialize;
241
+ this._processing = true;
242
+ const requestScope = new A_Scope({
243
+ name: `a-channel@scope:request:${A_IdentityHelper.generateTimeId()}`
244
+ });
245
+ const context = new A_ChannelRequestContext(params);
246
+ try {
247
+ requestScope.inherit(A_Context.scope(this));
248
+ requestScope.register(context);
249
+ await this.call("onBeforeRequest" /* onBeforeRequest */, requestScope);
250
+ await this.call("onRequest" /* onRequest */, requestScope);
251
+ await this.call("onAfterRequest" /* onAfterRequest */, requestScope);
252
+ this._processing = false;
253
+ return context;
254
+ } catch (error) {
255
+ this._processing = false;
256
+ const channelError = new A_ChannelError(error);
257
+ context.fail(channelError);
258
+ await this.call("onError" /* onError */, requestScope);
259
+ return context;
260
+ }
62
261
  }
63
262
  /**
64
- * Uses for Fire-and-Forget messaging through the channel
263
+ * Sends a fire-and-forget message (Send pattern).
264
+ *
265
+ * This method is used for one-way communication where no response is expected:
266
+ * - Event broadcasting
267
+ * - Notification sending
268
+ * - Message queuing
269
+ * - Logging operations
270
+ *
271
+ * The method follows this lifecycle:
272
+ * 1. Ensures channel is initialized
273
+ * 2. Creates send scope and context
274
+ * 3. Calls onSend hook
275
+ * 4. Completes without returning data
65
276
  *
66
- * @param message - can be of any type depending on the channel implementation
277
+ * If the operation fails, the onError hook is called but no error is thrown
278
+ * to the caller (fire-and-forget semantics).
279
+ *
280
+ * @template _ParamsType The type of message parameters
281
+ * @param message The message to send
282
+ * @returns {Promise<void>} Promise that resolves when send is complete
283
+ *
284
+ * @example
285
+ * ```typescript
286
+ * // Send notification
287
+ * await channel.send({
288
+ * type: 'user.login',
289
+ * userId: 'user-123',
290
+ * timestamp: new Date().toISOString()
291
+ * });
292
+ *
293
+ * // Send to message queue
294
+ * await channel.send({
295
+ * queue: 'email-queue',
296
+ * payload: {
297
+ * to: 'user@example.com',
298
+ * subject: 'Welcome!',
299
+ * body: 'Welcome to our service!'
300
+ * }
301
+ * });
302
+ * ```
67
303
  */
68
304
  async send(message) {
69
- throw new A_ChannelError(
70
- A_ChannelError.MethodNotImplemented,
71
- `The send method is not implemented in ${this.constructor.name} channel.`
72
- );
305
+ await this.initialize;
306
+ this._processing = true;
307
+ const requestScope = new A_Scope({
308
+ name: `a-channel@scope:send:${A_IdentityHelper.generateTimeId()}`
309
+ });
310
+ const context = new A_ChannelRequestContext(message);
311
+ try {
312
+ requestScope.inherit(A_Context.scope(this));
313
+ requestScope.register(context);
314
+ await this.call("onSend" /* onSend */, requestScope);
315
+ this._processing = false;
316
+ } catch (error) {
317
+ this._processing = false;
318
+ const channelError = new A_ChannelError(error);
319
+ context.fail(channelError);
320
+ await this.call("onError" /* onError */, requestScope);
321
+ }
322
+ }
323
+ /**
324
+ * @deprecated This method is deprecated and will be removed in future versions.
325
+ * Use request() or send() methods instead depending on your communication pattern.
326
+ *
327
+ * For request/response pattern: Use request()
328
+ * For fire-and-forget pattern: Use send()
329
+ * For consumer patterns: Implement custom consumer logic using request() in a loop
330
+ */
331
+ async consume() {
332
+ await this.initialize;
333
+ this._processing = true;
334
+ const requestScope = new A_Scope({ name: `a-channel@scope:consume:${A_IdentityHelper.generateTimeId()}` });
335
+ const context = new A_ChannelRequestContext();
336
+ try {
337
+ requestScope.inherit(A_Context.scope(this));
338
+ requestScope.register(context);
339
+ await this.call("onConsume" /* onConsume */, requestScope);
340
+ this._processing = false;
341
+ return context;
342
+ } catch (error) {
343
+ this._processing = false;
344
+ const channelError = new A_ChannelError(error);
345
+ context.fail(channelError);
346
+ await this.call("onError" /* onError */, requestScope);
347
+ return context;
348
+ }
73
349
  }
74
350
  };
75
351
  __decorateClass([
76
- A_Feature.Define()
77
- ], A_Channel.prototype, "connect", 1);
352
+ A_Feature.Extend({
353
+ name: "onConnect" /* onConnect */
354
+ })
355
+ ], A_Channel.prototype, "onConnect", 1);
356
+ __decorateClass([
357
+ A_Feature.Extend({
358
+ name: "onDisconnect" /* onDisconnect */
359
+ })
360
+ ], A_Channel.prototype, "onDisconnect", 1);
361
+ __decorateClass([
362
+ A_Feature.Extend({
363
+ name: "onBeforeRequest" /* onBeforeRequest */
364
+ })
365
+ ], A_Channel.prototype, "onBeforeRequest", 1);
78
366
  __decorateClass([
79
- A_Feature.Define()
80
- ], A_Channel.prototype, "request", 1);
367
+ A_Feature.Extend({
368
+ name: "onRequest" /* onRequest */
369
+ })
370
+ ], A_Channel.prototype, "onRequest", 1);
81
371
  __decorateClass([
82
- A_Feature.Define()
83
- ], A_Channel.prototype, "send", 1);
372
+ A_Feature.Extend({
373
+ name: "onAfterRequest" /* onAfterRequest */
374
+ })
375
+ ], A_Channel.prototype, "onAfterRequest", 1);
376
+ __decorateClass([
377
+ A_Feature.Extend({
378
+ name: "onError" /* onError */
379
+ })
380
+ ], A_Channel.prototype, "onError", 1);
381
+ __decorateClass([
382
+ A_Feature.Extend({
383
+ name: "onSend" /* onSend */
384
+ })
385
+ ], A_Channel.prototype, "onSend", 1);
84
386
 
85
387
  // src/lib/A-Command/A-Command.constants.ts
86
388
  var A_TYPES__CommandMetaKey = /* @__PURE__ */ ((A_TYPES__CommandMetaKey2) => {
@@ -186,6 +488,9 @@ var A_Memory = class extends A_Fragment {
186
488
  return obj;
187
489
  }
188
490
  };
491
+ var A_CommandError = class extends A_Error {
492
+ };
493
+ A_CommandError.CommandScopeBindingError = "A-Command Scope Binding Error";
189
494
 
190
495
  // src/lib/A-Command/A-Command.entity.ts
191
496
  var A_Command = class extends A_Entity {
@@ -291,9 +596,7 @@ var A_Command = class extends A_Entity {
291
596
  }
292
597
  this._status = "INITIALIZATION" /* INITIALIZATION */;
293
598
  this._startTime = /* @__PURE__ */ new Date();
294
- if (!this.scope.isInheritedFrom(A_Context.scope(this))) {
295
- this.scope.inherit(A_Context.scope(this));
296
- }
599
+ this.checkScopeInheritance();
297
600
  this.emit("init");
298
601
  await this.call("init", this.scope);
299
602
  this._status = "INITIALIZED" /* INITIALIZED */;
@@ -303,22 +606,34 @@ var A_Command = class extends A_Entity {
303
606
  if (this._status !== "INITIALIZED" /* INITIALIZED */) {
304
607
  return;
305
608
  }
609
+ this.checkScopeInheritance();
306
610
  this._status = "COMPILATION" /* COMPILATION */;
307
611
  this.emit("compile");
308
612
  await this.call("compile", this.scope);
309
613
  this._status = "COMPILED" /* COMPILED */;
310
614
  }
615
+ /**
616
+ * Processes the command execution
617
+ *
618
+ * @returns
619
+ */
620
+ async process() {
621
+ if (this._status !== "COMPILED" /* COMPILED */)
622
+ return;
623
+ this._status = "IN_PROGRESS" /* IN_PROGRESS */;
624
+ this.checkScopeInheritance();
625
+ this.emit("execute");
626
+ await this.call("execute", this.scope);
627
+ }
311
628
  /**
312
629
  * Executes the command logic.
313
630
  */
314
631
  async execute() {
632
+ this.checkScopeInheritance();
315
633
  try {
316
634
  await this.init();
317
635
  await this.compile();
318
- if (this._status === "COMPILED" /* COMPILED */) {
319
- this.emit("execute");
320
- await this.call("execute", this.scope);
321
- }
636
+ await this.process();
322
637
  await this.complete();
323
638
  } catch (error) {
324
639
  await this.fail();
@@ -328,6 +643,7 @@ var A_Command = class extends A_Entity {
328
643
  * Marks the command as completed
329
644
  */
330
645
  async complete() {
646
+ this.checkScopeInheritance();
331
647
  this._status = "COMPLETED" /* COMPLETED */;
332
648
  this._endTime = /* @__PURE__ */ new Date();
333
649
  this._result = this.scope.resolve(A_Memory).toJSON();
@@ -338,6 +654,7 @@ var A_Command = class extends A_Entity {
338
654
  * Marks the command as failed
339
655
  */
340
656
  async fail() {
657
+ this.checkScopeInheritance();
341
658
  this._status = "FAILED" /* FAILED */;
342
659
  this._endTime = /* @__PURE__ */ new Date();
343
660
  this._errors = this.scope.resolve(A_Memory).Errors;
@@ -440,8 +757,21 @@ var A_Command = class extends A_Entity {
440
757
  errors: this.errors ? Array.from(this.errors).map((err) => err.toJSON()) : void 0
441
758
  };
442
759
  }
443
- };
444
- var A_CommandError = class extends A_Error {
760
+ checkScopeInheritance() {
761
+ let attachedScope;
762
+ try {
763
+ attachedScope = A_Context.scope(this);
764
+ } catch (error) {
765
+ throw new A_CommandError({
766
+ title: A_CommandError.CommandScopeBindingError,
767
+ description: `Command ${this.code} is not bound to any context scope. Ensure the command is properly registered within a context before execution.`,
768
+ originalError: error
769
+ });
770
+ }
771
+ if (!this.scope.isInheritedFrom(A_Context.scope(this))) {
772
+ this.scope.inherit(A_Context.scope(this));
773
+ }
774
+ }
445
775
  };
446
776
 
447
777
  // src/lib/A-Config/A-Config.constants.ts