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