@adobe-commerce/aio-toolkit 1.2.2 → 1.2.4

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
@@ -5877,11 +5877,15 @@ var _AbdbCollection = class _AbdbCollection {
5877
5877
  const collection = await client.collection(this._name);
5878
5878
  return await callback(collection, client);
5879
5879
  } catch (error) {
5880
- if (error instanceof DbError) {
5881
- throw new Error(`AbdbCollection: database error: ${error.message}`);
5880
+ if (DbError && error instanceof DbError) {
5881
+ const dbErr = new Error(`AbdbCollection: database error: ${error.message}`);
5882
+ dbErr.cause = error;
5883
+ throw dbErr;
5882
5884
  }
5883
5885
  const detail = error instanceof Error ? error.message : String(error);
5884
- throw new Error(`AbdbCollection: unexpected error: ${detail}`);
5886
+ const unexpectedErr = new Error(`AbdbCollection: unexpected error: ${detail}`);
5887
+ unexpectedErr.cause = error;
5888
+ throw unexpectedErr;
5885
5889
  } finally {
5886
5890
  await client?.close();
5887
5891
  }
@@ -5974,13 +5978,31 @@ var _AbdbRepository = class _AbdbRepository {
5974
5978
  * Returns all documents matching `filter` as an array.
5975
5979
  * Passing no filter (or an empty object) returns every document in the collection.
5976
5980
  *
5981
+ * Pagination and sorting are applied in the following MongoDB cursor order:
5982
+ * `.find(filter)` → `.sort(...)` → `.skip(...)` → `.limit(...)` → `.toArray()`
5983
+ *
5977
5984
  * @param filter - Optional query filter (default: `{}`)
5985
+ * @param options - Optional pagination and sort configuration
5986
+ * @param options.sort - Column and direction to sort by (`'asc'` maps to `1`, `'desc'` to `-1`)
5987
+ * @param options.page_size - Maximum number of documents to return
5988
+ * @param options.current_page - 1-based page index; combined with `page_size` to compute `.skip()`
5978
5989
  * @returns Array of matched documents (may be empty)
5979
5990
  */
5980
- async find(filter = {}) {
5991
+ async find(filter = {}, options = {}) {
5981
5992
  return this._collection.run(
5982
5993
  async (collection) => {
5983
- return collection.find(filter).toArray();
5994
+ const { current_page, page_size, sort } = options;
5995
+ let cursor = collection.find(filter);
5996
+ if (sort?.column) {
5997
+ cursor = cursor.sort({ [sort.column]: sort.direction === "desc" ? -1 : 1 });
5998
+ }
5999
+ if (page_size !== void 0 && page_size > 0) {
6000
+ if (current_page !== void 0 && current_page > 1) {
6001
+ cursor = cursor.skip((current_page - 1) * page_size);
6002
+ }
6003
+ cursor = cursor.limit(page_size);
6004
+ }
6005
+ return cursor.toArray();
5984
6006
  },
5985
6007
  this._token,
5986
6008
  this._region
@@ -6401,7 +6423,7 @@ var _WebhookActionResponse = class _WebhookActionResponse {
6401
6423
  * processing the webhook. This helps with debugging and error tracking.
6402
6424
  *
6403
6425
  * @param message - Optional error message describing what went wrong
6404
- * @param exceptionClass - Optional exception class name for categorization (e.g., 'Magento\\Framework\\Exception\\LocalizedException')
6426
+ * @param exceptionType - Optional exception type name for categorization (e.g., 'Magento\\Framework\\Exception\\LocalizedException')
6405
6427
  * @returns An exception response object
6406
6428
  *
6407
6429
  * @example
@@ -6423,15 +6445,15 @@ var _WebhookActionResponse = class _WebhookActionResponse {
6423
6445
  * });
6424
6446
  * ```
6425
6447
  */
6426
- static exception(message, exceptionClass) {
6448
+ static exception(message, exceptionType) {
6427
6449
  const response = {
6428
6450
  op: "exception" /* EXCEPTION */
6429
6451
  };
6430
6452
  if (message !== void 0) {
6431
6453
  response.message = message;
6432
6454
  }
6433
- if (exceptionClass !== void 0) {
6434
- response.class = exceptionClass;
6455
+ if (exceptionType !== void 0) {
6456
+ response.type = exceptionType;
6435
6457
  }
6436
6458
  return response;
6437
6459
  }
@@ -11694,6 +11716,166 @@ __name(_OnboardCommerce, "OnboardCommerce");
11694
11716
  var OnboardCommerce = _OnboardCommerce;
11695
11717
  var onboard_commerce_default = OnboardCommerce;
11696
11718
 
11719
+ // src/integration/rabbit-mq-client/index.ts
11720
+ import amqplib from "amqplib";
11721
+ var _RabbitMQClient = class _RabbitMQClient {
11722
+ /**
11723
+ * @param credentials - AMQP connection credentials (host, port, username, password, vhost).
11724
+ */
11725
+ constructor(credentials) {
11726
+ this.credentials = credentials;
11727
+ }
11728
+ /**
11729
+ * Opens an AMQP connection, checks queue depth, and pulls up to
11730
+ * `effectiveBatch = min(batchSize, messageCount)` messages via channel.get.
11731
+ * Messages are processed in parallel windows of `maxParallel`. channel.get returns
11732
+ * false when the queue is empty, so the method exits deterministically without any
11733
+ * cancellation logic. The connection is always closed in a `finally` block.
11734
+ *
11735
+ * @param queueName - Name of the queue to consume from.
11736
+ * @param options - Consume configuration (batchSize, maxParallel, nackRequeue, exchange).
11737
+ * @param handler - Callback invoked with the queue name and decoded string content of each message.
11738
+ * @returns Counts of consumed, acked, and nacked messages; plus per-failure details in `errors`.
11739
+ * @throws Propagates connection, exchange-assertion, or queue-assertion errors to the caller.
11740
+ */
11741
+ async consume(queueName, options, handler) {
11742
+ const connection = await amqplib.connect(this.buildConnectionUrl());
11743
+ let channel;
11744
+ try {
11745
+ channel = await connection.createChannel();
11746
+ if (options.exchange) {
11747
+ await channel.assertExchange(options.exchange, "direct", { durable: true });
11748
+ await channel.assertQueue(queueName, { durable: true });
11749
+ await channel.bindQueue(queueName, options.exchange, queueName);
11750
+ } else {
11751
+ await channel.assertQueue(queueName, { durable: true });
11752
+ }
11753
+ return await this.processBatch(channel, queueName, options, handler);
11754
+ } finally {
11755
+ try {
11756
+ await channel?.close();
11757
+ } catch {
11758
+ }
11759
+ await connection.close();
11760
+ }
11761
+ }
11762
+ /**
11763
+ * Opens an AMQP connection, asserts the queue, and enqueues each payload in `payloads`.
11764
+ * Each payload is sent as a persistent message via `sendToQueue`. If the write buffer is
11765
+ * full (`sendToQueue` returns `false`), the message is still counted as published and the
11766
+ * method waits for the channel's `drain` event before continuing, to respect backpressure
11767
+ * without losing messages. Only genuine send errors (thrown exceptions) are counted as
11768
+ * failures. The connection is always closed in a `finally` block regardless of outcome.
11769
+ *
11770
+ * @param queueName - Name of the queue to publish to.
11771
+ * @param payloads - Array of string payloads to enqueue.
11772
+ * @returns Counts of published and failed messages; plus per-failure details in `errors`.
11773
+ * @throws Propagates connection or queue-assertion errors to the caller.
11774
+ */
11775
+ async publish(queueName, payloads) {
11776
+ const connection = await amqplib.connect(this.buildConnectionUrl());
11777
+ let channel;
11778
+ try {
11779
+ channel = await connection.createChannel();
11780
+ await channel.assertQueue(queueName, { durable: true });
11781
+ return await this.publishBatch(channel, queueName, payloads);
11782
+ } finally {
11783
+ try {
11784
+ await channel?.close();
11785
+ } catch {
11786
+ }
11787
+ await connection.close();
11788
+ }
11789
+ }
11790
+ /**
11791
+ * Sends each payload to the queue via `sendToQueue`. Tracks published and failed counts.
11792
+ * When `sendToQueue` returns `false` (write buffer full / backpressure), the message is
11793
+ * still counted as published — it is already accepted by the channel — and the loop
11794
+ * pauses until the channel emits `drain` before continuing, preventing unbounded memory
11795
+ * pressure. A payload is only counted as failed when `sendToQueue` throws an error.
11796
+ */
11797
+ async publishBatch(channel, queueName, payloads) {
11798
+ const stats = { published: 0, failed: 0, errors: [] };
11799
+ for (const payload of payloads) {
11800
+ try {
11801
+ const sent = channel.sendToQueue(queueName, Buffer.from(payload), { persistent: true });
11802
+ stats.published++;
11803
+ if (!sent) {
11804
+ await new Promise((resolve) => channel.once("drain", resolve));
11805
+ }
11806
+ } catch (error) {
11807
+ stats.failed++;
11808
+ stats.errors.push({ payload, error });
11809
+ }
11810
+ }
11811
+ return stats;
11812
+ }
11813
+ /**
11814
+ * Builds the AMQP connection URL from the stored credentials.
11815
+ * Uses `amqps://` when `secure` is true, `amqp://` otherwise.
11816
+ * Username, password, and vhost are percent-encoded to handle special characters.
11817
+ */
11818
+ buildConnectionUrl() {
11819
+ const { host, port, username, password, vhost, secure = false } = this.credentials;
11820
+ const protocol = secure ? "amqps" : "amqp";
11821
+ return `${protocol}://${encodeURIComponent(username)}:${encodeURIComponent(password)}@${host}:${port}/${encodeURIComponent(vhost)}`;
11822
+ }
11823
+ /**
11824
+ * Checks queue depth via channel.checkQueue, then pulls up to
11825
+ * `effectiveBatch = min(batchSize, messageCount)` messages using channel.get
11826
+ * (AMQP basic.get). Messages are processed in parallel windows of `maxParallel`:
11827
+ * each window pulls up to `maxParallel` messages sequentially, then processes them
11828
+ * concurrently via Promise.all before moving to the next window.
11829
+ *
11830
+ * channel.get returns false when the queue is empty, so the loop exits immediately
11831
+ * if siblings have drained the queue — no consumer registration, no cancel, no hang.
11832
+ */
11833
+ async processBatch(channel, queueName, options, handler) {
11834
+ const stats = { consumed: 0, acked: 0, nacked: 0, errors: [] };
11835
+ const { batchSize, maxParallel } = options;
11836
+ const { messageCount } = await channel.checkQueue(queueName);
11837
+ if (messageCount === 0) return stats;
11838
+ const effectiveBatch = Math.min(batchSize, messageCount);
11839
+ while (stats.consumed < effectiveBatch) {
11840
+ const windowSize = Math.min(maxParallel, effectiveBatch - stats.consumed);
11841
+ const window = [];
11842
+ for (let i = 0; i < windowSize; i++) {
11843
+ const msg = await channel.get(queueName, { noAck: false });
11844
+ if (!msg) break;
11845
+ window.push(msg);
11846
+ stats.consumed++;
11847
+ }
11848
+ if (window.length === 0) break;
11849
+ await Promise.all(
11850
+ window.map((msg) => this.processMessage(channel, queueName, msg, stats, options, handler))
11851
+ );
11852
+ if (window.length < windowSize) break;
11853
+ }
11854
+ return stats;
11855
+ }
11856
+ /**
11857
+ * Invokes `handler` with the queue name and decoded message content. Acks on success.
11858
+ * On failure, nacks the message and records the content and error in `stats.errors`.
11859
+ * Whether to requeue on nack is controlled by `options.nackRequeue` (default: true).
11860
+ */
11861
+ async processMessage(channel, queueName, msg, stats, options, handler) {
11862
+ const { nackRequeue = true } = options;
11863
+ const content = msg.content.toString();
11864
+ try {
11865
+ await handler(queueName, content);
11866
+ channel.ack(msg);
11867
+ stats.acked++;
11868
+ } catch (error) {
11869
+ channel.nack(msg, false, nackRequeue);
11870
+ stats.errors.push({ content, error });
11871
+ stats.nacked++;
11872
+ }
11873
+ }
11874
+ };
11875
+ __name(_RabbitMQClient, "RabbitMQClient");
11876
+ var RabbitMQClient = _RabbitMQClient;
11877
+ var rabbit_mq_client_default = RabbitMQClient;
11878
+
11697
11879
  // src/commerce/adobe-commerce-client/index.ts
11698
11880
  import got from "got";
11699
11881
  var _AdobeCommerceClient = class _AdobeCommerceClient {
@@ -12713,6 +12895,7 @@ export {
12713
12895
  parameters_default as Parameters,
12714
12896
  provider_default as ProviderManager,
12715
12897
  publish_event_default as PublishEvent,
12898
+ rabbit_mq_client_default as RabbitMQClient,
12716
12899
  registration_default as RegistrationManager,
12717
12900
  rest_client_default as RestClient,
12718
12901
  runtime_action_default as RuntimeAction,