sqs-partial-batch-processor 0.3.7 → 0.4.1

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/README.md CHANGED
@@ -8,13 +8,17 @@ You supply per-record async logic; the library handles looping, per-record error
8
8
 
9
9
  This library intentionally does **not** parse message bodies, validate schemas, create AWS SDK clients, or make retry/business decisions for you.
10
10
 
11
+ Import the public API from the package root (`processPartialBatch`, `processPartialBatchWithResult`, `ProcessPartialBatchOptions`, `ProcessRecordResult`).
12
+
11
13
  ## Features
12
14
 
13
15
  - Implements Lambda SQS **partial batch response** pattern (only failed messages are retried).
14
16
  - Per-record error boundary (failures are isolated to each record).
15
17
  - Aggregates failed identifiers into `batchItemFailures` (defaults to `messageId`).
16
18
  - Throw-style (`processPartialBatch`) and Result-style (`processPartialBatchWithResult`) APIs.
19
+ - Typed Result callback via `ProcessRecordResult` (`{ ok: true }` | `{ ok: false }`).
17
20
  - Optional bounded concurrency (`concurrency`), error hook (`onRecordError`), and custom `itemIdentifier` mapping (`mapMessageId`).
21
+ - Shared options type `ProcessPartialBatchOptions` for both APIs.
18
22
  - For `{ ok: false }`, `onRecordError` receives an `Error` that includes the resolved `itemIdentifier` (message and `cause`) for easier debugging.
19
23
 
20
24
  ## Requirements
@@ -34,6 +38,10 @@ npm install sqs-partial-batch-processor
34
38
  yarn add sqs-partial-batch-processor
35
39
  ```
36
40
 
41
+ ```bash
42
+ pnpm add sqs-partial-batch-processor
43
+ ```
44
+
37
45
  ## Usage
38
46
 
39
47
  Throw to mark a record as failed:
@@ -49,14 +57,21 @@ export const handler = async (event: SQSEvent) =>
49
57
  });
50
58
  ```
51
59
 
52
- Result-style callback (no throw for control flow):
60
+ Result-style callback (no throw for control flow). Return a `ProcessRecordResult`:
61
+
62
+ - `{ ok: true }`: success (not listed in `batchItemFailures`)
63
+ - `{ ok: false }`: failure (listed in `batchItemFailures`)
64
+ - thrown errors: still treated as failures
53
65
 
54
66
  ```ts
55
67
  import type { SQSEvent } from 'aws-lambda';
56
- import { processPartialBatchWithResult } from 'sqs-partial-batch-processor';
68
+ import {
69
+ processPartialBatchWithResult,
70
+ type ProcessRecordResult,
71
+ } from 'sqs-partial-batch-processor';
57
72
 
58
73
  export const handler = async (event: SQSEvent) =>
59
- processPartialBatchWithResult(event, async (record) => {
74
+ processPartialBatchWithResult(event, async (record): Promise<ProcessRecordResult> => {
60
75
  if (record.body === '') {
61
76
  return { ok: false };
62
77
  }
@@ -66,7 +81,7 @@ export const handler = async (event: SQSEvent) =>
66
81
 
67
82
  ## Options
68
83
 
69
- Both `processPartialBatch` and `processPartialBatchWithResult` accept an optional `options` object:
84
+ Both `processPartialBatch` and `processPartialBatchWithResult` accept an optional `ProcessPartialBatchOptions` object:
70
85
 
71
86
  - `concurrency?: number` (default: `1`): maximum parallelism. Must be a finite integer `>= 1` (`1` = sequential, `> 1` = bounded concurrency). If invalid, the function throws (`RangeError` for `< 1`, `TypeError` for non-integer / non-finite).
72
87
  - **Tip**: start with `1` and increase gradually while watching downstream limits (external API rate limits, DB connection pools, and Lambda reserved concurrency). SQS batches are typically small, so a large value rarely helps.
@@ -77,8 +92,8 @@ Both `processPartialBatch` and `processPartialBatchWithResult` accept an optiona
77
92
  - Example (structured log / metrics hook):
78
93
 
79
94
  ```ts
80
- import { processPartialBatch } from 'sqs-partial-batch-processor';
81
95
  import type { SQSEvent } from 'aws-lambda';
96
+ import { processPartialBatch } from 'sqs-partial-batch-processor';
82
97
 
83
98
  export const handler = async (event: SQSEvent) =>
84
99
  processPartialBatch(
package/lib/index.d.ts CHANGED
@@ -1,73 +1,3 @@
1
- import type { SQSBatchResponse, SQSEvent, SQSRecord } from 'aws-lambda';
2
- /**
3
- * Options for {@link processPartialBatch} and {@link processPartialBatchWithResult}.
4
- */
5
- export interface ProcessPartialBatchOptions {
6
- /**
7
- * Maximum number of records processed in parallel.
8
- *
9
- * - `1`: sequential processing
10
- * - `> 1`: bounded concurrency pool (order of {@link SQSBatchResponse.batchItemFailures} is not guaranteed)
11
- *
12
- * @default 1
13
- * @throws {RangeError} When `concurrency` is less than 1.
14
- * @throws {TypeError} When `concurrency` is not a finite integer.
15
- */
16
- readonly concurrency?: number;
17
- /**
18
- * Called when a record is treated as failed (thrown error or `{ ok: false }`).
19
- * Use for logging or metrics; the library does not write to `console` by default.
20
- *
21
- * For `{ ok: false }` from {@link processPartialBatchWithResult}, `error` is an `Error`
22
- * whose message includes the resolved `itemIdentifier`, and whose `cause` is
23
- * `{ itemIdentifier }` for structured logging.
24
- *
25
- * Do not log `record.body` as-is — it may contain secrets or personal data.
26
- * Prefer identifiers such as `record.messageId` (or your `mapMessageId` result)
27
- * and a sanitized error summary.
28
- *
29
- * @param record The failed SQS record.
30
- * @param error The thrown value, or a synthesized `Error` when `{ ok: false }` was returned.
31
- */
32
- readonly onRecordError?: (record: SQSRecord, error: unknown) => void;
33
- /**
34
- * Returns the `itemIdentifier` reported in `batchItemFailures` for a record.
35
- * Defaults to `record.messageId` when omitted.
36
- *
37
- * @param record The SQS record being processed.
38
- * @returns The identifier sent back to Lambda in `batchItemFailures`.
39
- */
40
- readonly mapMessageId?: (record: SQSRecord) => string;
41
- }
42
- /**
43
- * Runs `processRecord` for each SQS record. Thrown errors are mapped to
44
- * `batchItemFailures`; successful records are not listed.
45
- *
46
- * @param event The SQS Lambda event.
47
- * @param processRecord Per-record handler. Throw to mark only that record as failed.
48
- * @param options Optional concurrency, error hook, and message id mapping.
49
- * @returns An {@link SQSBatchResponse} listing only failed `itemIdentifier`s.
50
- * @throws {RangeError} When `options.concurrency` is less than 1.
51
- * @throws {TypeError} When `options.concurrency` is not a finite integer.
52
- */
53
- export declare const processPartialBatch: (event: SQSEvent, processRecord: (record: SQSRecord) => Promise<void>, options?: ProcessPartialBatchOptions) => Promise<SQSBatchResponse>;
54
- /**
55
- * Like {@link processPartialBatch}, but uses a Result-style callback (no throw for control flow).
56
- *
57
- * - `{ ok: true }`: success (not listed in `batchItemFailures`)
58
- * - `{ ok: false }`: failure; if `onRecordError` is set, it receives an `Error` whose
59
- * message includes the resolved `itemIdentifier`, and whose `cause` is `{ itemIdentifier }`
60
- * - thrown errors: still treated as failures; `onRecordError` receives the thrown value when set
61
- *
62
- * @param event The SQS Lambda event.
63
- * @param processRecord Per-record handler returning `{ ok: true }` or `{ ok: false }`.
64
- * @param options Optional concurrency, error hook, and message id mapping.
65
- * @returns An {@link SQSBatchResponse} listing only failed `itemIdentifier`s.
66
- * @throws {RangeError} When `options.concurrency` is less than 1.
67
- * @throws {TypeError} When `options.concurrency` is not a finite integer.
68
- */
69
- export declare const processPartialBatchWithResult: (event: SQSEvent, processRecord: (record: SQSRecord) => Promise<{
70
- ok: true;
71
- } | {
72
- ok: false;
73
- }>, options?: ProcessPartialBatchOptions) => Promise<SQSBatchResponse>;
1
+ export type { ProcessPartialBatchOptions, ProcessRecordResult } from './types';
2
+ export { processPartialBatch } from './processor/process-partial-batch';
3
+ export { processPartialBatchWithResult } from './processor/process-partial-batch-with-result';
package/lib/index.js CHANGED
@@ -1,176 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.processPartialBatchWithResult = exports.processPartialBatch = void 0;
4
- /**
5
- * Creates a single `batchItemFailures` entry for a failed record.
6
- *
7
- * @param itemIdentifier The identifier reported back to Lambda.
8
- * @returns An object suitable for {@link SQSBatchResponse.batchItemFailures}.
9
- */
10
- const itemFailure = (itemIdentifier) => ({
11
- itemIdentifier,
12
- });
13
- /**
14
- * Resolves and validates the `concurrency` option.
15
- *
16
- * @param value User-provided concurrency.
17
- * @returns A validated concurrency value (defaults to `1`).
18
- * @throws {RangeError} When `value` is less than 1.
19
- * @throws {TypeError} When `value` is not a finite integer.
20
- */
21
- const resolveConcurrency = (value) => {
22
- if (value === undefined) {
23
- return 1;
24
- }
25
- if (!Number.isFinite(value) || !Number.isInteger(value)) {
26
- throw new TypeError('concurrency must be a finite integer');
27
- }
28
- if (value < 1) {
29
- throw new RangeError('concurrency must be >= 1');
30
- }
31
- return value;
32
- };
33
- /**
34
- * Runs `fn` over `items` with at most `concurrency` parallel workers.
35
- * Each worker pulls the next index until none remain.
36
- *
37
- * @param items Items to process.
38
- * @param concurrency Maximum parallel workers (must be `>= 1`).
39
- * @param fn Async handler invoked for each item.
40
- * @returns A promise that resolves when every item has been processed.
41
- */
42
- const runWithConcurrency = async (items, concurrency, fn) => {
43
- if (items.length === 0) {
44
- return;
45
- }
46
- const limit = Math.min(concurrency, items.length);
47
- let next = 0;
48
- /**
49
- * Claims the next work index, or `undefined` when the queue is empty.
50
- *
51
- * @returns The next index to process, or `undefined` if none remain.
52
- */
53
- const takeNextIndex = () => {
54
- const index = next;
55
- next += 1;
56
- if (index >= items.length) {
57
- return undefined;
58
- }
59
- return index;
60
- };
61
- const workers = [];
62
- for (let w = 0; w < limit; w++) {
63
- workers.push((async () => {
64
- let workIndex = takeNextIndex();
65
- while (workIndex !== undefined) {
66
- const item = items[workIndex];
67
- if (item === undefined) {
68
- return;
69
- }
70
- await fn(item);
71
- workIndex = takeNextIndex();
72
- }
73
- })());
74
- }
75
- await Promise.all(workers);
76
- };
77
- /**
78
- * Runs `processRecord` for each SQS record. Thrown errors are mapped to
79
- * `batchItemFailures`; successful records are not listed.
80
- *
81
- * @param event The SQS Lambda event.
82
- * @param processRecord Per-record handler. Throw to mark only that record as failed.
83
- * @param options Optional concurrency, error hook, and message id mapping.
84
- * @returns An {@link SQSBatchResponse} listing only failed `itemIdentifier`s.
85
- * @throws {RangeError} When `options.concurrency` is less than 1.
86
- * @throws {TypeError} When `options.concurrency` is not a finite integer.
87
- */
88
- const processPartialBatch = async (event, processRecord, options) => {
89
- const concurrency = resolveConcurrency(options?.concurrency);
90
- const batchItemFailures = [];
91
- /**
92
- * Processes one record and records a batch item failure on error.
93
- *
94
- * @param record The SQS record to process.
95
- */
96
- const handle = async (record) => {
97
- const id = options?.mapMessageId?.(record) ?? record.messageId;
98
- try {
99
- await processRecord(record);
100
- }
101
- catch (error) {
102
- if (options?.onRecordError) {
103
- options.onRecordError(record, error);
104
- }
105
- batchItemFailures.push(itemFailure(id));
106
- }
107
- };
108
- if (concurrency <= 1) {
109
- for (const record of event.Records) {
110
- await handle(record);
111
- }
112
- }
113
- else {
114
- await runWithConcurrency(event.Records, concurrency, handle);
115
- }
116
- return { batchItemFailures };
117
- };
118
- exports.processPartialBatch = processPartialBatch;
119
- /**
120
- * Like {@link processPartialBatch}, but uses a Result-style callback (no throw for control flow).
121
- *
122
- * - `{ ok: true }`: success (not listed in `batchItemFailures`)
123
- * - `{ ok: false }`: failure; if `onRecordError` is set, it receives an `Error` whose
124
- * message includes the resolved `itemIdentifier`, and whose `cause` is `{ itemIdentifier }`
125
- * - thrown errors: still treated as failures; `onRecordError` receives the thrown value when set
126
- *
127
- * @param event The SQS Lambda event.
128
- * @param processRecord Per-record handler returning `{ ok: true }` or `{ ok: false }`.
129
- * @param options Optional concurrency, error hook, and message id mapping.
130
- * @returns An {@link SQSBatchResponse} listing only failed `itemIdentifier`s.
131
- * @throws {RangeError} When `options.concurrency` is less than 1.
132
- * @throws {TypeError} When `options.concurrency` is not a finite integer.
133
- */
134
- const processPartialBatchWithResult = async (event, processRecord, options) => {
135
- const concurrency = resolveConcurrency(options?.concurrency);
136
- const batchItemFailures = [];
137
- /**
138
- * Processes one record; maps throws and `{ ok: false }` to batch item failures.
139
- *
140
- * @param record The SQS record to process.
141
- */
142
- const handle = async (record) => {
143
- const id = options?.mapMessageId?.(record) ?? record.messageId;
144
- let result;
145
- try {
146
- result = await processRecord(record);
147
- }
148
- catch (error) {
149
- if (options?.onRecordError) {
150
- options.onRecordError(record, error);
151
- }
152
- batchItemFailures.push(itemFailure(id));
153
- return;
154
- }
155
- if (result.ok) {
156
- return;
157
- }
158
- if (options?.onRecordError) {
159
- const error = new Error(`processRecord returned { ok: false } (itemIdentifier=${id})`);
160
- Object.assign(error, { cause: { itemIdentifier: id } });
161
- options.onRecordError(record, error);
162
- }
163
- batchItemFailures.push(itemFailure(id));
164
- };
165
- if (concurrency <= 1) {
166
- for (const record of event.Records) {
167
- await handle(record);
168
- }
169
- }
170
- else {
171
- await runWithConcurrency(event.Records, concurrency, handle);
172
- }
173
- return { batchItemFailures };
174
- };
175
- exports.processPartialBatchWithResult = processPartialBatchWithResult;
176
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBNkNBOzs7OztHQUtHO0FBQ0gsTUFBTSxXQUFXLEdBQUcsQ0FBQyxjQUFzQixFQUE4QixFQUFFLENBQUMsQ0FBQztJQUMzRSxjQUFjO0NBQ2YsQ0FBQyxDQUFDO0FBRUg7Ozs7Ozs7R0FPRztBQUNILE1BQU0sa0JBQWtCLEdBQUcsQ0FBQyxLQUF5QixFQUFVLEVBQUU7SUFDL0QsSUFBSSxLQUFLLEtBQUssU0FBUyxFQUFFLENBQUM7UUFDeEIsT0FBTyxDQUFDLENBQUM7SUFDWCxDQUFDO0lBQ0QsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDeEQsTUFBTSxJQUFJLFNBQVMsQ0FBQyxzQ0FBc0MsQ0FBQyxDQUFDO0lBQzlELENBQUM7SUFDRCxJQUFJLEtBQUssR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUNkLE1BQU0sSUFBSSxVQUFVLENBQUMsMEJBQTBCLENBQUMsQ0FBQztJQUNuRCxDQUFDO0lBQ0QsT0FBTyxLQUFLLENBQUM7QUFDZixDQUFDLENBQUM7QUFFRjs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sa0JBQWtCLEdBQUcsS0FBSyxFQUM5QixLQUFtQixFQUNuQixXQUFtQixFQUNuQixFQUE4QixFQUNmLEVBQUU7SUFDakIsSUFBSSxLQUFLLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ3ZCLE9BQU87SUFDVCxDQUFDO0lBQ0QsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ2xELElBQUksSUFBSSxHQUFHLENBQUMsQ0FBQztJQUViOzs7O09BSUc7SUFDSCxNQUFNLGFBQWEsR0FBRyxHQUF1QixFQUFF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QztRQUN2QixDQUFDO0lBQ0gsQ0FBQztTQUFNLENBQUM7UUFDTixNQUFNLGtCQUFrQixDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQy9ELENBQUM7SUFFRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsQ0FBQztBQUMvQixDQUFDLENBQUM7QUE3Q1csUUFBQSw2QkFBNkIsaUNBNkN4QyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgU1FTQmF0Y2hSZXNwb25zZSwgU1FTRXZlbnQsIFNRU1JlY29yZCB9IGZyb20gJ2F3cy1sYW1iZGEnO1xuXG4vKipcbiAqIE9wdGlvbnMgZm9yIHtAbGluayBwcm9jZXNzUGFydGlhbEJhdGNofSBhbmQge0BsaW5rIHByb2Nlc3NQYXJ0aWFsQmF0Y2hXaXRoUmVzdWx0fS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBQcm9jZXNzUGFydGlhbEJhdGNoT3B0aW9ucyB7XG4gIC8qKlxuICAgKiBNYXhpbXVtIG51bWJlciBvZiByZWNvcmRzIHByb2Nlc3NlZCBpbiBwYXJhbGxlbC5cbiAgICpcbiAgICogLSBgMWA6IHNlcXVlbnRpYWwgcHJvY2Vzc2luZ1xuICAgKiAtIGA+IDFgOiBib3VuZGVkIGNvbmN1cnJlbmN5IHBvb2wgKG9yZGVyIG9mIHtAbGluayBTUVNCYXRjaFJlc3BvbnNlLmJhdGNoSXRlbUZhaWx1cmVzfSBpcyBub3QgZ3VhcmFudGVlZClcbiAgICpcbiAgICogQGRlZmF1bHQgMVxuICAgKiBAdGhyb3dzIHtSYW5nZUVycm9yfSBXaGVuIGBjb25jdXJyZW5jeWAgaXMgbGVzcyB0aGFuIDEuXG4gICAqIEB0aHJvd3Mge1R5cGVFcnJvcn0gV2hlbiBgY29uY3VycmVuY3lgIGlzIG5vdCBhIGZpbml0ZSBpbnRlZ2VyLlxuICAgKi9cbiAgcmVhZG9ubHkgY29uY3VycmVuY3k/OiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIENhbGxlZCB3aGVuIGEgcmVjb3JkIGlzIHRyZWF0ZWQgYXMgZmFpbGVkICh0aHJvd24gZXJyb3Igb3IgYHsgb2s6IGZhbHNlIH1gKS5cbiAgICogVXNlIGZvciBsb2dnaW5nIG9yIG1ldHJpY3M7IHRoZSBsaWJyYXJ5IGRvZXMgbm90IHdyaXRlIHRvIGBjb25zb2xlYCBieSBkZWZhdWx0LlxuICAgKlxuICAgKiBGb3IgYHsgb2s6IGZhbHNlIH1gIGZyb20ge0BsaW5rIHByb2Nlc3NQYXJ0aWFsQmF0Y2hXaXRoUmVzdWx0fSwgYGVycm9yYCBpcyBhbiBgRXJyb3JgXG4gICAqIHdob3NlIG1lc3NhZ2UgaW5jbHVkZXMgdGhlIHJlc29sdmVkIGBpdGVtSWRlbnRpZmllcmAsIGFuZCB3aG9zZSBgY2F1c2VgIGlzXG4gICAqIGB7IGl0ZW1JZGVudGlmaWVyIH1gIGZvciBzdHJ1Y3R1cmVkIGxvZ2dpbmcuXG4gICAqXG4gICAqIERvIG5vdCBsb2cgYHJlY29yZC5ib2R5YCBhcy1pcyDigJQgaXQgbWF5IGNvbnRhaW4gc2VjcmV0cyBvciBwZXJzb25hbCBkYXRhLlxuICAgKiBQcmVmZXIgaWRlbnRpZmllcnMgc3VjaCBhcyBgcmVjb3JkLm1lc3NhZ2VJZGAgKG9yIHlvdXIgYG1hcE1lc3NhZ2VJZGAgcmVzdWx0KVxuICAgKiBhbmQgYSBzYW5pdGl6ZWQgZXJyb3Igc3VtbWFyeS5cbiAgICpcbiAgICogQHBhcmFtIHJlY29yZCBUaGUgZmFpbGVkIFNRUyByZWNvcmQuXG4gICAqIEBwYXJhbSBlcnJvciBUaGUgdGhyb3duIHZhbHVlLCBvciBhIHN5bnRoZXNpemVkIGBFcnJvcmAgd2hlbiBgeyBvazogZmFsc2UgfWAgd2FzIHJldHVybmVkLlxuICAgKi9cbiAgcmVhZG9ubHkgb25SZWNvcmRFcnJvcj86IChyZWNvcmQ6IFNRU1JlY29yZCwgZXJyb3I6IHVua25vd24pID0+IHZvaWQ7XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIGBpdGVtSWRlbnRpZmllcmAgcmVwb3J0ZWQgaW4gYGJhdGNoSXRlbUZhaWx1cmVzYCBmb3IgYSByZWNvcmQuXG4gICAqIERlZmF1bHRzIHRvIGByZWNvcmQubWVzc2FnZUlkYCB3aGVuIG9taXR0ZWQuXG4gICAqXG4gICAqIEBwYXJhbSByZWNvcmQgVGhlIFNRUyByZWNvcmQgYmVpbmcgcHJvY2Vzc2VkLlxuICAgKiBAcmV0dXJucyBUaGUgaWRlbnRpZmllciBzZW50IGJhY2sgdG8gTGFtYmRhIGluIGBiYXRjaEl0ZW1GYWlsdXJlc2AuXG4gICAqL1xuICByZWFkb25seSBtYXBNZXNzYWdlSWQ/OiAocmVjb3JkOiBTUVNSZWNvcmQpID0+IHN0cmluZztcbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgc2luZ2xlIGBiYXRjaEl0ZW1GYWlsdXJlc2AgZW50cnkgZm9yIGEgZmFpbGVkIHJlY29yZC5cbiAqXG4gKiBAcGFyYW0gaXRlbUlkZW50aWZpZXIgVGhlIGlkZW50aWZpZXIgcmVwb3J0ZWQgYmFjayB0byBMYW1iZGEuXG4gKiBAcmV0dXJucyBBbiBvYmplY3Qgc3VpdGFibGUgZm9yIHtAbGluayBTUVNCYXRjaFJlc3BvbnNlLmJhdGNoSXRlbUZhaWx1cmVzfS5cbiAqL1xuY29uc3QgaXRlbUZhaWx1cmUgPSAoaXRlbUlkZW50aWZpZXI6IHN0cmluZyk6IHsgaXRlbUlkZW50aWZpZXI6IHN0cmluZyB9ID0+ICh7XG4gIGl0ZW1JZGVudGlmaWVyLFxufSk7XG5cbi8qKlxuICogUmVzb2x2ZXMgYW5kIHZhbGlkYXRlcyB0aGUgYGNvbmN1cnJlbmN5YCBvcHRpb24uXG4gKlxuICogQHBhcmFtIHZhbHVlIFVzZXItcHJvdmlkZWQgY29uY3VycmVuY3kuXG4gKiBAcmV0dXJucyBBIHZhbGlkYXRlZCBjb25jdXJyZW5jeSB2YWx1ZSAoZGVmYXVsdHMgdG8gYDFgKS5cbiAqIEB0aHJvd3Mge1JhbmdlRXJyb3J9IFdoZW4gYHZhbHVlYCBpcyBsZXNzIHRoYW4gMS5cbiAqIEB0aHJvd3Mge1R5cGVFcnJvcn0gV2hlbiBgdmFsdWVgIGlzIG5vdCBhIGZpbml0ZSBpbnRlZ2VyLlxuICovXG5jb25zdCByZXNvbHZlQ29uY3VycmVuY3kgPSAodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCk6IG51bWJlciA9PiB7XG4gIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgcmV0dXJuIDE7XG4gIH1cbiAgaWYgKCFOdW1iZXIuaXNGaW5pdGUodmFsdWUpIHx8ICFOdW1iZXIuaXNJbnRlZ2VyKHZhbHVlKSkge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ2NvbmN1cnJlbmN5IG11c3QgYmUgYSBmaW5pdGUgaW50ZWdlcicpO1xuICB9XG4gIGlmICh2YWx1ZSA8IDEpIHtcbiAgICB0aHJvdyBuZXcgUmFuZ2VFcnJvcignY29uY3VycmVuY3kgbXVzdCBiZSA+PSAxJyk7XG4gIH1cbiAgcmV0dXJuIHZhbHVlO1xufTtcblxuLyoqXG4gKiBSdW5zIGBmbmAgb3ZlciBgaXRlbXNgIHdpdGggYXQgbW9zdCBgY29uY3VycmVuY3lgIHBhcmFsbGVsIHdvcmtlcnMuXG4gKiBFYWNoIHdvcmtlciBwdWxscyB0aGUgbmV4dCBpbmRleCB1bnRpbCBub25lIHJlbWFpbi5cbiAqXG4gKiBAcGFyYW0gaXRlbXMgSXRlbXMgdG8gcHJvY2Vzcy5cbiAqIEBwYXJhbSBjb25jdXJyZW5jeSBNYXhpbXVtIHBhcmFsbGVsIHdvcmtlcnMgKG11c3QgYmUgYD49IDFgKS5cbiAqIEBwYXJhbSBmbiBBc3luYyBoYW5kbGVyIGludm9rZWQgZm9yIGVhY2ggaXRlbS5cbiAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gZXZlcnkgaXRlbSBoYXMgYmVlbiBwcm9jZXNzZWQuXG4gKi9cbmNvbnN0IHJ1bldpdGhDb25jdXJyZW5jeSA9IGFzeW5jIDxUPihcbiAgaXRlbXM6IHJlYWRvbmx5IFRbXSxcbiAgY29uY3VycmVuY3k6IG51bWJlcixcbiAgZm46IChpdGVtOiBUKSA9PiBQcm9taXNlPHZvaWQ+LFxuKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gIGlmIChpdGVtcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm47XG4gIH1cbiAgY29uc3QgbGltaXQgPSBNYXRoLm1pbihjb25jdXJyZW5jeSwgaXRlbXMubGVuZ3RoKTtcbiAgbGV0IG5leHQgPSAwO1xuXG4gIC8qKlxuICAgKiBDbGFpbXMgdGhlIG5leHQgd29yayBpbmRleCwgb3IgYHVuZGVmaW5lZGAgd2hlbiB0aGUgcXVldWUgaXMgZW1wdHkuXG4gICAqXG4gICAqIEByZXR1cm5zIFRoZSBuZXh0IGluZGV4IHRvIHByb2Nlc3MsIG9yIGB1bmRlZmluZWRgIGlmIG5vbmUgcmVtYWluLlxuICAgKi9cbiAgY29uc3QgdGFrZU5leHRJbmRleCA9ICgpOiBudW1iZXIgfCB1bmRlZmluZWQgPT4ge1xuICAgIGNvbnN0IGluZGV4ID0gbmV4dDtcbiAgICBuZXh0ICs9IDE7XG4gICAgaWYgKGluZGV4ID49IGl0ZW1zLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gICAgcmV0dXJuIGluZGV4O1xuICB9O1xuXG4gIGNvbnN0IHdvcmtlcnM6IFByb21pc2U8dm9pZD5bXSA9IFtdO1xuICBmb3IgKGxldCB3ID0gMDsgdyA8IGxpbWl0OyB3KyspIHtcbiAgICB3b3JrZXJzLnB1c2goKGFzeW5jICgpID0+IHtcbiAgICAgIGxldCB3b3JrSW5kZXggPSB0YWtlTmV4dEluZGV4KCk7XG4gICAgICB3aGlsZSAod29ya0luZGV4ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgY29uc3QgaXRlbSA9IGl0ZW1zW3dvcmtJbmRleF07XG4gICAgICAgIGlmIChpdGVtID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgYXdhaXQgZm4oaXRlbSk7XG4gICAgICAgIHdvcmtJbmRleCA9IHRha2VOZXh0SW5kZXgoKTtcbiAgICAgIH1cbiAgICB9KSgpKTtcbiAgfVxuICBhd2FpdCBQcm9taXNlLmFsbCh3b3JrZXJzKTtcbn07XG5cbi8qKlxuICogUnVucyBgcHJvY2Vzc1JlY29yZGAgZm9yIGVhY2ggU1FTIHJlY29yZC4gVGhyb3duIGVycm9ycyBhcmUgbWFwcGVkIHRvXG4gKiBgYmF0Y2hJdGVtRmFpbHVyZXNgOyBzdWNjZXNzZnVsIHJlY29yZHMgYXJlIG5vdCBsaXN0ZWQuXG4gKlxuICogQHBhcmFtIGV2ZW50IFRoZSBTUVMgTGFtYmRhIGV2ZW50LlxuICogQHBhcmFtIHByb2Nlc3NSZWNvcmQgUGVyLXJlY29yZCBoYW5kbGVyLiBUaHJvdyB0byBtYXJrIG9ubHkgdGhhdCByZWNvcmQgYXMgZmFpbGVkLlxuICogQHBhcmFtIG9wdGlvbnMgT3B0aW9uYWwgY29uY3VycmVuY3ksIGVycm9yIGhvb2ssIGFuZCBtZXNzYWdlIGlkIG1hcHBpbmcuXG4gKiBAcmV0dXJucyBBbiB7QGxpbmsgU1FTQmF0Y2hSZXNwb25zZX0gbGlzdGluZyBvbmx5IGZhaWxlZCBgaXRlbUlkZW50aWZpZXJgcy5cbiAqIEB0aHJvd3Mge1JhbmdlRXJyb3J9IFdoZW4gYG9wdGlvbnMuY29uY3VycmVuY3lgIGlzIGxlc3MgdGhhbiAxLlxuICogQHRocm93cyB7VHlwZUVycm9yfSBXaGVuIGBvcHRpb25zLmNvbmN1cnJlbmN5YCBpcyBub3QgYSBmaW5pdGUgaW50ZWdlci5cbiAqL1xuZXhwb3J0IGNvbnN0IHByb2Nlc3NQYXJ0aWFsQmF0Y2ggPSBhc3luYyAoXG4gIGV2ZW50OiBTUVNFdmVudCxcbiAgcHJvY2Vzc1JlY29yZDogKHJlY29yZDogU1FTUmVjb3JkKSA9PiBQcm9taXNlPHZvaWQ+LFxuICBvcHRpb25zPzogUHJvY2Vzc1BhcnRpYWxCYXRjaE9wdGlvbnMsXG4pOiBQcm9taXNlPFNRU0JhdGNoUmVzcG9uc2U+ID0+IHtcbiAgY29uc3QgY29uY3VycmVuY3kgPSByZXNvbHZlQ29uY3VycmVuY3kob3B0aW9ucz8uY29uY3VycmVuY3kpO1xuICBjb25zdCBiYXRjaEl0ZW1GYWlsdXJlczogeyBpdGVtSWRlbnRpZmllcjogc3RyaW5nIH1bXSA9IFtdO1xuXG4gIC8qKlxuICAgKiBQcm9jZXNzZXMgb25lIHJlY29yZCBhbmQgcmVjb3JkcyBhIGJhdGNoIGl0ZW0gZmFpbHVyZSBvbiBlcnJvci5cbiAgICpcbiAgICogQHBhcmFtIHJlY29yZCBUaGUgU1FTIHJlY29yZCB0byBwcm9jZXNzLlxuICAgKi9cbiAgY29uc3QgaGFuZGxlID0gYXN5bmMgKHJlY29yZDogU1FTUmVjb3JkKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgY29uc3QgaWQgPSBvcHRpb25zPy5tYXBNZXNzYWdlSWQ/LihyZWNvcmQpID8/IHJlY29yZC5tZXNzYWdlSWQ7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHByb2Nlc3NSZWNvcmQocmVjb3JkKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKG9wdGlvbnM/Lm9uUmVjb3JkRXJyb3IpIHtcbiAgICAgICAgb3B0aW9ucy5vblJlY29yZEVycm9yKHJlY29yZCwgZXJyb3IpO1xuICAgICAgfVxuICAgICAgYmF0Y2hJdGVtRmFpbHVyZXMucHVzaChpdGVtRmFpbHVyZShpZCkpO1xuICAgIH1cbiAgfTtcblxuICBpZiAoY29uY3VycmVuY3kgPD0gMSkge1xuICAgIGZvciAoY29uc3QgcmVjb3JkIG9mIGV2ZW50LlJlY29yZHMpIHtcbiAgICAgIGF3YWl0IGhhbmRsZShyZWNvcmQpO1xuICAgIH1cbiAgfSBlbHNlIHtcbiAgICBhd2FpdCBydW5XaXRoQ29uY3VycmVuY3koZXZlbnQuUmVjb3JkcywgY29uY3VycmVuY3ksIGhhbmRsZSk7XG4gIH1cblxuICByZXR1cm4geyBiYXRjaEl0ZW1GYWlsdXJlcyB9O1xufTtcblxuLyoqXG4gKiBMaWtlIHtAbGluayBwcm9jZXNzUGFydGlhbEJhdGNofSwgYnV0IHVzZXMgYSBSZXN1bHQtc3R5bGUgY2FsbGJhY2sgKG5vIHRocm93IGZvciBjb250cm9sIGZsb3cpLlxuICpcbiAqIC0gYHsgb2s6IHRydWUgfWA6IHN1Y2Nlc3MgKG5vdCBsaXN0ZWQgaW4gYGJhdGNoSXRlbUZhaWx1cmVzYClcbiAqIC0gYHsgb2s6IGZhbHNlIH1gOiBmYWlsdXJlOyBpZiBgb25SZWNvcmRFcnJvcmAgaXMgc2V0LCBpdCByZWNlaXZlcyBhbiBgRXJyb3JgIHdob3NlXG4gKiAgIG1lc3NhZ2UgaW5jbHVkZXMgdGhlIHJlc29sdmVkIGBpdGVtSWRlbnRpZmllcmAsIGFuZCB3aG9zZSBgY2F1c2VgIGlzIGB7IGl0ZW1JZGVudGlmaWVyIH1gXG4gKiAtIHRocm93biBlcnJvcnM6IHN0aWxsIHRyZWF0ZWQgYXMgZmFpbHVyZXM7IGBvblJlY29yZEVycm9yYCByZWNlaXZlcyB0aGUgdGhyb3duIHZhbHVlIHdoZW4gc2V0XG4gKlxuICogQHBhcmFtIGV2ZW50IFRoZSBTUVMgTGFtYmRhIGV2ZW50LlxuICogQHBhcmFtIHByb2Nlc3NSZWNvcmQgUGVyLXJlY29yZCBoYW5kbGVyIHJldHVybmluZyBgeyBvazogdHJ1ZSB9YCBvciBgeyBvazogZmFsc2UgfWAuXG4gKiBAcGFyYW0gb3B0aW9ucyBPcHRpb25hbCBjb25jdXJyZW5jeSwgZXJyb3IgaG9vaywgYW5kIG1lc3NhZ2UgaWQgbWFwcGluZy5cbiAqIEByZXR1cm5zIEFuIHtAbGluayBTUVNCYXRjaFJlc3BvbnNlfSBsaXN0aW5nIG9ubHkgZmFpbGVkIGBpdGVtSWRlbnRpZmllcmBzLlxuICogQHRocm93cyB7UmFuZ2VFcnJvcn0gV2hlbiBgb3B0aW9ucy5jb25jdXJyZW5jeWAgaXMgbGVzcyB0aGFuIDEuXG4gKiBAdGhyb3dzIHtUeXBlRXJyb3J9IFdoZW4gYG9wdGlvbnMuY29uY3VycmVuY3lgIGlzIG5vdCBhIGZpbml0ZSBpbnRlZ2VyLlxuICovXG5leHBvcnQgY29uc3QgcHJvY2Vzc1BhcnRpYWxCYXRjaFdpdGhSZXN1bHQgPSBhc3luYyAoXG4gIGV2ZW50OiBTUVNFdmVudCxcbiAgcHJvY2Vzc1JlY29yZDogKHJlY29yZDogU1FTUmVjb3JkKSA9PiBQcm9taXNlPHsgb2s6IHRydWUgfSB8IHsgb2s6IGZhbHNlIH0+LFxuICBvcHRpb25zPzogUHJvY2Vzc1BhcnRpYWxCYXRjaE9wdGlvbnMsXG4pOiBQcm9taXNlPFNRU0JhdGNoUmVzcG9uc2U+ID0+IHtcbiAgY29uc3QgY29uY3VycmVuY3kgPSByZXNvbHZlQ29uY3VycmVuY3kob3B0aW9ucz8uY29uY3VycmVuY3kpO1xuICBjb25zdCBiYXRjaEl0ZW1GYWlsdXJlczogeyBpdGVtSWRlbnRpZmllcjogc3RyaW5nIH1bXSA9IFtdO1xuXG4gIC8qKlxuICAgKiBQcm9jZXNzZXMgb25lIHJlY29yZDsgbWFwcyB0aHJvd3MgYW5kIGB7IG9rOiBmYWxzZSB9YCB0byBiYXRjaCBpdGVtIGZhaWx1cmVzLlxuICAgKlxuICAgKiBAcGFyYW0gcmVjb3JkIFRoZSBTUVMgcmVjb3JkIHRvIHByb2Nlc3MuXG4gICAqL1xuICBjb25zdCBoYW5kbGUgPSBhc3luYyAocmVjb3JkOiBTUVNSZWNvcmQpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICBjb25zdCBpZCA9IG9wdGlvbnM/Lm1hcE1lc3NhZ2VJZD8uKHJlY29yZCkgPz8gcmVjb3JkLm1lc3NhZ2VJZDtcbiAgICBsZXQgcmVzdWx0OiB7IG9rOiB0cnVlIH0gfCB7IG9rOiBmYWxzZSB9O1xuICAgIHRyeSB7XG4gICAgICByZXN1bHQgPSBhd2FpdCBwcm9jZXNzUmVjb3JkKHJlY29yZCk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChvcHRpb25zPy5vblJlY29yZEVycm9yKSB7XG4gICAgICAgIG9wdGlvbnMub25SZWNvcmRFcnJvcihyZWNvcmQsIGVycm9yKTtcbiAgICAgIH1cbiAgICAgIGJhdGNoSXRlbUZhaWx1cmVzLnB1c2goaXRlbUZhaWx1cmUoaWQpKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHJlc3VsdC5vaykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAob3B0aW9ucz8ub25SZWNvcmRFcnJvcikge1xuICAgICAgY29uc3QgZXJyb3IgPSBuZXcgRXJyb3IoYHByb2Nlc3NSZWNvcmQgcmV0dXJuZWQgeyBvazogZmFsc2UgfSAoaXRlbUlkZW50aWZpZXI9JHtpZH0pYCk7XG4gICAgICBPYmplY3QuYXNzaWduKGVycm9yLCB7IGNhdXNlOiB7IGl0ZW1JZGVudGlmaWVyOiBpZCB9IH0pO1xuICAgICAgb3B0aW9ucy5vblJlY29yZEVycm9yKHJlY29yZCwgZXJyb3IpO1xuICAgIH1cbiAgICBiYXRjaEl0ZW1GYWlsdXJlcy5wdXNoKGl0ZW1GYWlsdXJlKGlkKSk7XG4gIH07XG5cbiAgaWYgKGNvbmN1cnJlbmN5IDw9IDEpIHtcbiAgICBmb3IgKGNvbnN0IHJlY29yZCBvZiBldmVudC5SZWNvcmRzKSB7XG4gICAgICBhd2FpdCBoYW5kbGUocmVjb3JkKTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgYXdhaXQgcnVuV2l0aENvbmN1cnJlbmN5KGV2ZW50LlJlY29yZHMsIGNvbmN1cnJlbmN5LCBoYW5kbGUpO1xuICB9XG5cbiAgcmV0dXJuIHsgYmF0Y2hJdGVtRmFpbHVyZXMgfTtcbn07XG4iXX0=
4
+ var process_partial_batch_1 = require("./processor/process-partial-batch");
5
+ Object.defineProperty(exports, "processPartialBatch", { enumerable: true, get: function () { return process_partial_batch_1.processPartialBatch; } });
6
+ var process_partial_batch_with_result_1 = require("./processor/process-partial-batch-with-result");
7
+ Object.defineProperty(exports, "processPartialBatchWithResult", { enumerable: true, get: function () { return process_partial_batch_with_result_1.processPartialBatchWithResult; } });
8
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQ0EsMkVBQXdFO0FBQS9ELDRIQUFBLG1CQUFtQixPQUFBO0FBQzVCLG1HQUE4RjtBQUFyRixrSkFBQSw2QkFBNkIsT0FBQSIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCB0eXBlIHsgUHJvY2Vzc1BhcnRpYWxCYXRjaE9wdGlvbnMsIFByb2Nlc3NSZWNvcmRSZXN1bHQgfSBmcm9tICcuL3R5cGVzJztcbmV4cG9ydCB7IHByb2Nlc3NQYXJ0aWFsQmF0Y2ggfSBmcm9tICcuL3Byb2Nlc3Nvci9wcm9jZXNzLXBhcnRpYWwtYmF0Y2gnO1xuZXhwb3J0IHsgcHJvY2Vzc1BhcnRpYWxCYXRjaFdpdGhSZXN1bHQgfSBmcm9tICcuL3Byb2Nlc3Nvci9wcm9jZXNzLXBhcnRpYWwtYmF0Y2gtd2l0aC1yZXN1bHQnO1xuIl19
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Resolves and validates the `concurrency` option.
3
+ *
4
+ * @param value User-provided concurrency.
5
+ * @returns A validated concurrency value (defaults to `1`).
6
+ * @throws {RangeError} When `value` is less than 1.
7
+ * @throws {TypeError} When `value` is not a finite integer.
8
+ */
9
+ export declare const resolveConcurrency: (value: number | undefined) => number;
10
+ /**
11
+ * Runs `fn` over `items` with at most `concurrency` parallel workers.
12
+ * Each worker pulls the next index until none remain.
13
+ *
14
+ * @param items Items to process.
15
+ * @param concurrency Maximum parallel workers (must be `>= 1`).
16
+ * @param fn Async handler invoked for each item.
17
+ * @returns A promise that resolves when every item has been processed.
18
+ */
19
+ export declare const runWithConcurrency: <T>(items: readonly T[], concurrency: number, fn: (item: T) => Promise<void>) => Promise<void>;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runWithConcurrency = exports.resolveConcurrency = void 0;
4
+ /**
5
+ * Returns whether a worker still has an index to process.
6
+ *
7
+ * @param workIndex The index claimed from the work queue, or `undefined` when empty.
8
+ * @returns `true` when `workIndex` is a remaining item index.
9
+ */
10
+ const hasRemainingWork = (workIndex) => workIndex !== undefined;
11
+ /**
12
+ * Resolves and validates the `concurrency` option.
13
+ *
14
+ * @param value User-provided concurrency.
15
+ * @returns A validated concurrency value (defaults to `1`).
16
+ * @throws {RangeError} When `value` is less than 1.
17
+ * @throws {TypeError} When `value` is not a finite integer.
18
+ */
19
+ const resolveConcurrency = (value) => {
20
+ if (value === undefined) {
21
+ return 1;
22
+ }
23
+ if (!Number.isFinite(value) || !Number.isInteger(value)) {
24
+ throw new TypeError('concurrency must be a finite integer');
25
+ }
26
+ if (value < 1) {
27
+ throw new RangeError('concurrency must be >= 1');
28
+ }
29
+ return value;
30
+ };
31
+ exports.resolveConcurrency = resolveConcurrency;
32
+ /**
33
+ * Runs `fn` over `items` with at most `concurrency` parallel workers.
34
+ * Each worker pulls the next index until none remain.
35
+ *
36
+ * @param items Items to process.
37
+ * @param concurrency Maximum parallel workers (must be `>= 1`).
38
+ * @param fn Async handler invoked for each item.
39
+ * @returns A promise that resolves when every item has been processed.
40
+ */
41
+ const runWithConcurrency = async (items, concurrency, fn) => {
42
+ if (items.length === 0) {
43
+ return;
44
+ }
45
+ const limit = Math.min(concurrency, items.length);
46
+ let next = 0;
47
+ /**
48
+ * Claims the next work index, or `undefined` when the queue is empty.
49
+ *
50
+ * @returns The next index to process, or `undefined` if none remain.
51
+ */
52
+ const takeNextIndex = () => {
53
+ const index = next;
54
+ next += 1;
55
+ if (index >= items.length) {
56
+ return undefined;
57
+ }
58
+ return index;
59
+ };
60
+ const workers = [];
61
+ for (let w = 0; w < limit; w++) {
62
+ workers.push((async () => {
63
+ let workIndex = takeNextIndex();
64
+ while (hasRemainingWork(workIndex)) {
65
+ const item = items[workIndex];
66
+ if (item === undefined) {
67
+ return;
68
+ }
69
+ await fn(item);
70
+ workIndex = takeNextIndex();
71
+ }
72
+ })());
73
+ }
74
+ await Promise.all(workers);
75
+ };
76
+ exports.runWithConcurrency = runWithConcurrency;
77
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uY3VycmVuY3kuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcHJvY2Vzc29yL2NvbmN1cnJlbmN5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBOzs7OztHQUtHO0FBQ0gsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDLFNBQTZCLEVBQXVCLEVBQUUsQ0FDOUUsU0FBUyxLQUFLLFNBQVMsQ0FBQztBQUUxQjs7Ozs7OztHQU9HO0FBQ0ksTUFBTSxrQkFBa0IsR0FBRyxDQUFDLEtBQXlCLEVBQVUsRUFBRTtJQUN0RSxJQUFJLEtBQUssS0FBSyxTQUFTLEVBQUUsQ0FBQztRQUN4QixPQUFPLENBQUMsQ0FBQztJQUNYLENBQUM7SUFDRCxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN4RCxNQUFNLElBQUksU0FBUyxDQUFDLHNDQUFzQyxDQUFDLENBQUM7SUFDOUQsQ0FBQztJQUNELElBQUksS0FBSyxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ2QsTUFBTSxJQUFJLFVBQVUsQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO0lBQ25ELENBQUM7SUFDRCxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUMsQ0FBQztBQVhXLFFBQUEsa0JBQWtCLHNCQVc3QjtBQUVGOzs7Ozs7OztHQVFHO0FBQ0ksTUFBTSxrQkFBa0IsR0FBRyxLQUFLLEVBQ3JDLEtBQW1CLEVBQ25CLFdBQW1CLEVBQ25CLEVBQThCLEVBQ2YsRUFBRTtJQUNqQixJQUFJLEtBQUssQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDdkIsT0FBTztJQUNULENBQUM7SUFDRCxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDbEQsSUFBSSxJQUFJLEdBQUcsQ0FBQyxDQUFDO0lBRWI7Ozs7T0FJRztJQUNILE1BQU0sYUFBYSxHQUFHLEdBQXVCLEVBQUU7UUFDN0MsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDO1FBQ25CLElBQUksSUFBSSxDQUFDLENBQUM7UUFDVixJQUFJLEtBQUssSUFBSSxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDMUIsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQztRQUNELE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQyxDQUFDO0lBRUYsTUFBTSxPQUFPLEdBQW9CLEVBQUUsQ0FBQztJQUNwQyxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsS0FBSyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDL0IsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLEtBQUssSUFBSSxFQUFFO1lBQ3ZCLElBQUksU0FBUyxHQUFHLGFBQWEsRUFBRSxDQUFDO1lBQ2hDLE9BQU8sZ0JBQWdCLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztnQkFDbkMsTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLFNBQVMsQ0FBQyxDQUFDO2dCQUM5QixJQUFJLElBQUksS0FBSyxTQUFTLEVBQUUsQ0FBQztvQkFDdkIsT0FBTztnQkFDVCxDQUFDO2dCQUNELE1BQU0sRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUNmLFNBQVMsR0FBRyxhQUFhLEVBQUUsQ0FBQztZQUM5QixDQUFDO1FBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ1IsQ0FBQztJQUNELE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQztBQUM3QixDQUFDLENBQUM7QUF4Q1csUUFBQSxrQkFBa0Isc0JBd0M3QiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUmV0dXJucyB3aGV0aGVyIGEgd29ya2VyIHN0aWxsIGhhcyBhbiBpbmRleCB0byBwcm9jZXNzLlxuICpcbiAqIEBwYXJhbSB3b3JrSW5kZXggVGhlIGluZGV4IGNsYWltZWQgZnJvbSB0aGUgd29yayBxdWV1ZSwgb3IgYHVuZGVmaW5lZGAgd2hlbiBlbXB0eS5cbiAqIEByZXR1cm5zIGB0cnVlYCB3aGVuIGB3b3JrSW5kZXhgIGlzIGEgcmVtYWluaW5nIGl0ZW0gaW5kZXguXG4gKi9cbmNvbnN0IGhhc1JlbWFpbmluZ1dvcmsgPSAod29ya0luZGV4OiBudW1iZXIgfCB1bmRlZmluZWQpOiB3b3JrSW5kZXggaXMgbnVtYmVyID0+XG4gIHdvcmtJbmRleCAhPT0gdW5kZWZpbmVkO1xuXG4vKipcbiAqIFJlc29sdmVzIGFuZCB2YWxpZGF0ZXMgdGhlIGBjb25jdXJyZW5jeWAgb3B0aW9uLlxuICpcbiAqIEBwYXJhbSB2YWx1ZSBVc2VyLXByb3ZpZGVkIGNvbmN1cnJlbmN5LlxuICogQHJldHVybnMgQSB2YWxpZGF0ZWQgY29uY3VycmVuY3kgdmFsdWUgKGRlZmF1bHRzIHRvIGAxYCkuXG4gKiBAdGhyb3dzIHtSYW5nZUVycm9yfSBXaGVuIGB2YWx1ZWAgaXMgbGVzcyB0aGFuIDEuXG4gKiBAdGhyb3dzIHtUeXBlRXJyb3J9IFdoZW4gYHZhbHVlYCBpcyBub3QgYSBmaW5pdGUgaW50ZWdlci5cbiAqL1xuZXhwb3J0IGNvbnN0IHJlc29sdmVDb25jdXJyZW5jeSA9ICh2YWx1ZTogbnVtYmVyIHwgdW5kZWZpbmVkKTogbnVtYmVyID0+IHtcbiAgaWYgKHZhbHVlID09PSB1bmRlZmluZWQpIHtcbiAgICByZXR1cm4gMTtcbiAgfVxuICBpZiAoIU51bWJlci5pc0Zpbml0ZSh2YWx1ZSkgfHwgIU51bWJlci5pc0ludGVnZXIodmFsdWUpKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcignY29uY3VycmVuY3kgbXVzdCBiZSBhIGZpbml0ZSBpbnRlZ2VyJyk7XG4gIH1cbiAgaWYgKHZhbHVlIDwgMSkge1xuICAgIHRocm93IG5ldyBSYW5nZUVycm9yKCdjb25jdXJyZW5jeSBtdXN0IGJlID49IDEnKTtcbiAgfVxuICByZXR1cm4gdmFsdWU7XG59O1xuXG4vKipcbiAqIFJ1bnMgYGZuYCBvdmVyIGBpdGVtc2Agd2l0aCBhdCBtb3N0IGBjb25jdXJyZW5jeWAgcGFyYWxsZWwgd29ya2Vycy5cbiAqIEVhY2ggd29ya2VyIHB1bGxzIHRoZSBuZXh0IGluZGV4IHVudGlsIG5vbmUgcmVtYWluLlxuICpcbiAqIEBwYXJhbSBpdGVtcyBJdGVtcyB0byBwcm9jZXNzLlxuICogQHBhcmFtIGNvbmN1cnJlbmN5IE1heGltdW0gcGFyYWxsZWwgd29ya2VycyAobXVzdCBiZSBgPj0gMWApLlxuICogQHBhcmFtIGZuIEFzeW5jIGhhbmRsZXIgaW52b2tlZCBmb3IgZWFjaCBpdGVtLlxuICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiBldmVyeSBpdGVtIGhhcyBiZWVuIHByb2Nlc3NlZC5cbiAqL1xuZXhwb3J0IGNvbnN0IHJ1bldpdGhDb25jdXJyZW5jeSA9IGFzeW5jIDxUPihcbiAgaXRlbXM6IHJlYWRvbmx5IFRbXSxcbiAgY29uY3VycmVuY3k6IG51bWJlcixcbiAgZm46IChpdGVtOiBUKSA9PiBQcm9taXNlPHZvaWQ+LFxuKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gIGlmIChpdGVtcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm47XG4gIH1cbiAgY29uc3QgbGltaXQgPSBNYXRoLm1pbihjb25jdXJyZW5jeSwgaXRlbXMubGVuZ3RoKTtcbiAgbGV0IG5leHQgPSAwO1xuXG4gIC8qKlxuICAgKiBDbGFpbXMgdGhlIG5leHQgd29yayBpbmRleCwgb3IgYHVuZGVmaW5lZGAgd2hlbiB0aGUgcXVldWUgaXMgZW1wdHkuXG4gICAqXG4gICAqIEByZXR1cm5zIFRoZSBuZXh0IGluZGV4IHRvIHByb2Nlc3MsIG9yIGB1bmRlZmluZWRgIGlmIG5vbmUgcmVtYWluLlxuICAgKi9cbiAgY29uc3QgdGFrZU5leHRJbmRleCA9ICgpOiBudW1iZXIgfCB1bmRlZmluZWQgPT4ge1xuICAgIGNvbnN0IGluZGV4ID0gbmV4dDtcbiAgICBuZXh0ICs9IDE7XG4gICAgaWYgKGluZGV4ID49IGl0ZW1zLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gICAgcmV0dXJuIGluZGV4O1xuICB9O1xuXG4gIGNvbnN0IHdvcmtlcnM6IFByb21pc2U8dm9pZD5bXSA9IFtdO1xuICBmb3IgKGxldCB3ID0gMDsgdyA8IGxpbWl0OyB3KyspIHtcbiAgICB3b3JrZXJzLnB1c2goKGFzeW5jICgpID0+IHtcbiAgICAgIGxldCB3b3JrSW5kZXggPSB0YWtlTmV4dEluZGV4KCk7XG4gICAgICB3aGlsZSAoaGFzUmVtYWluaW5nV29yayh3b3JrSW5kZXgpKSB7XG4gICAgICAgIGNvbnN0IGl0ZW0gPSBpdGVtc1t3b3JrSW5kZXhdO1xuICAgICAgICBpZiAoaXRlbSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IGZuKGl0ZW0pO1xuICAgICAgICB3b3JrSW5kZXggPSB0YWtlTmV4dEluZGV4KCk7XG4gICAgICB9XG4gICAgfSkoKSk7XG4gIH1cbiAgYXdhaXQgUHJvbWlzZS5hbGwod29ya2Vycyk7XG59O1xuIl19
@@ -0,0 +1,8 @@
1
+ import type { SQSBatchResponse } from 'aws-lambda';
2
+ /**
3
+ * Creates a single `batchItemFailures` entry for a failed record.
4
+ *
5
+ * @param itemIdentifier The identifier reported back to Lambda.
6
+ * @returns An object suitable for {@link SQSBatchResponse.batchItemFailures}.
7
+ */
8
+ export declare const itemFailure: (itemIdentifier: string) => SQSBatchResponse["batchItemFailures"][number];
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.itemFailure = void 0;
4
+ /**
5
+ * Creates a single `batchItemFailures` entry for a failed record.
6
+ *
7
+ * @param itemIdentifier The identifier reported back to Lambda.
8
+ * @returns An object suitable for {@link SQSBatchResponse.batchItemFailures}.
9
+ */
10
+ const itemFailure = (itemIdentifier) => ({
11
+ itemIdentifier,
12
+ });
13
+ exports.itemFailure = itemFailure;
14
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaXRlbS1mYWlsdXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Byb2Nlc3Nvci9pdGVtLWZhaWx1cmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBRUE7Ozs7O0dBS0c7QUFDSSxNQUFNLFdBQVcsR0FBRyxDQUN6QixjQUFzQixFQUN5QixFQUFFLENBQUMsQ0FBQztJQUNuRCxjQUFjO0NBQ2YsQ0FBQyxDQUFDO0FBSlUsUUFBQSxXQUFXLGVBSXJCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBTUVNCYXRjaFJlc3BvbnNlIH0gZnJvbSAnYXdzLWxhbWJkYSc7XG5cbi8qKlxuICogQ3JlYXRlcyBhIHNpbmdsZSBgYmF0Y2hJdGVtRmFpbHVyZXNgIGVudHJ5IGZvciBhIGZhaWxlZCByZWNvcmQuXG4gKlxuICogQHBhcmFtIGl0ZW1JZGVudGlmaWVyIFRoZSBpZGVudGlmaWVyIHJlcG9ydGVkIGJhY2sgdG8gTGFtYmRhLlxuICogQHJldHVybnMgQW4gb2JqZWN0IHN1aXRhYmxlIGZvciB7QGxpbmsgU1FTQmF0Y2hSZXNwb25zZS5iYXRjaEl0ZW1GYWlsdXJlc30uXG4gKi9cbmV4cG9ydCBjb25zdCBpdGVtRmFpbHVyZSA9IChcbiAgaXRlbUlkZW50aWZpZXI6IHN0cmluZyxcbik6IFNRU0JhdGNoUmVzcG9uc2VbJ2JhdGNoSXRlbUZhaWx1cmVzJ11bbnVtYmVyXSA9PiAoe1xuICBpdGVtSWRlbnRpZmllcixcbn0pO1xuIl19
@@ -0,0 +1,18 @@
1
+ import type { SQSBatchResponse, SQSEvent, SQSRecord } from 'aws-lambda';
2
+ import type { ProcessPartialBatchOptions, ProcessRecordResult } from '../types';
3
+ /**
4
+ * Like {@link processPartialBatch}, but uses a Result-style callback (no throw for control flow).
5
+ *
6
+ * - `{ ok: true }`: success (not listed in `batchItemFailures`)
7
+ * - `{ ok: false }`: failure; if `onRecordError` is set, it receives an `Error` whose
8
+ * message includes the resolved `itemIdentifier`, and whose `cause` is `{ itemIdentifier }`
9
+ * - thrown errors: still treated as failures; `onRecordError` receives the thrown value when set
10
+ *
11
+ * @param event The SQS Lambda event.
12
+ * @param processRecord Per-record handler returning `{ ok: true }` or `{ ok: false }`.
13
+ * @param options Optional concurrency, error hook, and message id mapping.
14
+ * @returns An {@link SQSBatchResponse} listing only failed `itemIdentifier`s.
15
+ * @throws {RangeError} When `options.concurrency` is less than 1.
16
+ * @throws {TypeError} When `options.concurrency` is not a finite integer.
17
+ */
18
+ export declare const processPartialBatchWithResult: (event: SQSEvent, processRecord: (record: SQSRecord) => Promise<ProcessRecordResult>, options?: ProcessPartialBatchOptions) => Promise<SQSBatchResponse>;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processPartialBatchWithResult = void 0;
4
+ const concurrency_1 = require("./concurrency");
5
+ const item_failure_1 = require("./item-failure");
6
+ /**
7
+ * Like {@link processPartialBatch}, but uses a Result-style callback (no throw for control flow).
8
+ *
9
+ * - `{ ok: true }`: success (not listed in `batchItemFailures`)
10
+ * - `{ ok: false }`: failure; if `onRecordError` is set, it receives an `Error` whose
11
+ * message includes the resolved `itemIdentifier`, and whose `cause` is `{ itemIdentifier }`
12
+ * - thrown errors: still treated as failures; `onRecordError` receives the thrown value when set
13
+ *
14
+ * @param event The SQS Lambda event.
15
+ * @param processRecord Per-record handler returning `{ ok: true }` or `{ ok: false }`.
16
+ * @param options Optional concurrency, error hook, and message id mapping.
17
+ * @returns An {@link SQSBatchResponse} listing only failed `itemIdentifier`s.
18
+ * @throws {RangeError} When `options.concurrency` is less than 1.
19
+ * @throws {TypeError} When `options.concurrency` is not a finite integer.
20
+ */
21
+ const processPartialBatchWithResult = async (event, processRecord, options) => {
22
+ const concurrency = (0, concurrency_1.resolveConcurrency)(options?.concurrency);
23
+ const batchItemFailures = [];
24
+ /**
25
+ * Processes one record; maps throws and `{ ok: false }` to batch item failures.
26
+ *
27
+ * @param record The SQS record to process.
28
+ */
29
+ const handle = async (record) => {
30
+ const id = options?.mapMessageId?.(record) ?? record.messageId;
31
+ let result;
32
+ try {
33
+ result = await processRecord(record);
34
+ }
35
+ catch (error) {
36
+ if (options?.onRecordError) {
37
+ options.onRecordError(record, error);
38
+ }
39
+ batchItemFailures.push((0, item_failure_1.itemFailure)(id));
40
+ return;
41
+ }
42
+ if (result.ok) {
43
+ return;
44
+ }
45
+ if (options?.onRecordError) {
46
+ const error = new Error(`processRecord returned { ok: false } (itemIdentifier=${id})`);
47
+ Object.assign(error, { cause: { itemIdentifier: id } });
48
+ options.onRecordError(record, error);
49
+ }
50
+ batchItemFailures.push((0, item_failure_1.itemFailure)(id));
51
+ };
52
+ if (concurrency <= 1) {
53
+ for (const record of event.Records) {
54
+ await handle(record);
55
+ }
56
+ }
57
+ else {
58
+ await (0, concurrency_1.runWithConcurrency)(event.Records, concurrency, handle);
59
+ }
60
+ return { batchItemFailures };
61
+ };
62
+ exports.processPartialBatchWithResult = processPartialBatchWithResult;
63
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvY2Vzcy1wYXJ0aWFsLWJhdGNoLXdpdGgtcmVzdWx0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Byb2Nlc3Nvci9wcm9jZXNzLXBhcnRpYWwtYmF0Y2gtd2l0aC1yZXN1bHQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBRUEsK0NBQXVFO0FBQ3ZFLGlEQUE2QztBQUU3Qzs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUNJLE1BQU0sNkJBQTZCLEdBQUcsS0FBSyxFQUNoRCxLQUFlLEVBQ2YsYUFBa0UsRUFDbEUsT0FBb0MsRUFDVCxFQUFFO0lBQzdCLE1BQU0sV0FBVyxHQUFHLElBQUEsZ0NBQWtCLEVBQUMsT0FBTyxFQUFFLFdBQVcsQ0FBQyxDQUFDO0lBQzdELE1BQU0saUJBQWlCLEdBQWlDLEVBQUUsQ0FBQztJQUUzRDs7OztPQUlHO0lBQ0gsTUFBTSxNQUFNLEdBQUcsS0FBSyxFQUFFLE1BQWlCLEVBQWlCLEVBQUU7UUFDeEQsTUFBTSxFQUFFLEdBQUcsT0FBTyxFQUFFLFlBQVksRUFBRSxDQUFDLE1BQU0sQ0FBQyxJQUFJLE1BQU0sQ0FBQyxTQUFTLENBQUM7UUFDL0QsSUFBSSxNQUEyQixDQUFDO1FBQ2hDLElBQUksQ0FBQztZQUNILE1BQU0sR0FBRyxNQUFNLGFBQWEsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUN2QyxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLElBQUksT0FBTyxFQUFFLGFBQWEsRUFBRSxDQUFDO2dCQUMzQixPQUFPLENBQUMsYUFBYSxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsQ0FBQztZQUN2QyxDQUFDO1lBQ0QsaUJBQWlCLENBQUMsSUFBSSxDQUFDLElBQUEsMEJBQVcsRUFBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQ3hDLE9BQU87UUFDVCxDQUFDO1FBQ0QsSUFBSSxNQUFNLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDZCxPQUFPO1FBQ1QsQ0FBQztRQUNELElBQUksT0FBTyxFQUFFLGFBQWEsRUFBRSxDQUFDO1lBQzNCLE1BQU0sS0FBSyxHQUFHLElBQUksS0FBSyxDQUFDLHdEQUF3RCxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQ3ZGLE1BQU0sQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLEVBQUUsS0FBSyxFQUFFLEVBQUUsY0FBYyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztZQUN4RCxPQUFPLENBQUMsYUFBYSxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsQ0FBQztRQUN2QyxDQUFDO1FBQ0QsaUJBQWlCLENBQUMsSUFBSSxDQUFDLElBQUEsMEJBQVcsRUFBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzFDLENBQUMsQ0FBQztJQUVGLElBQUksV0FBVyxJQUFJLENBQUMsRUFBRSxDQUFDO1FBQ3JCLEtBQUssTUFBTSxNQUFNLElBQUksS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ25DLE1BQU0sTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3ZCLENBQUM7SUFDSCxDQUFDO1NBQU0sQ0FBQztRQUNOLE1BQU0sSUFBQSxnQ0FBa0IsRUFBQyxLQUFLLENBQUMsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUMvRCxDQUFDO0lBRUQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLENBQUM7QUFDL0IsQ0FBQyxDQUFDO0FBN0NXLFFBQUEsNkJBQTZCLGlDQTZDeEMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IFNRU0JhdGNoUmVzcG9uc2UsIFNRU0V2ZW50LCBTUVNSZWNvcmQgfSBmcm9tICdhd3MtbGFtYmRhJztcbmltcG9ydCB0eXBlIHsgUHJvY2Vzc1BhcnRpYWxCYXRjaE9wdGlvbnMsIFByb2Nlc3NSZWNvcmRSZXN1bHQgfSBmcm9tICcuLi90eXBlcyc7XG5pbXBvcnQgeyByZXNvbHZlQ29uY3VycmVuY3ksIHJ1bldpdGhDb25jdXJyZW5jeSB9IGZyb20gJy4vY29uY3VycmVuY3knO1xuaW1wb3J0IHsgaXRlbUZhaWx1cmUgfSBmcm9tICcuL2l0ZW0tZmFpbHVyZSc7XG5cbi8qKlxuICogTGlrZSB7QGxpbmsgcHJvY2Vzc1BhcnRpYWxCYXRjaH0sIGJ1dCB1c2VzIGEgUmVzdWx0LXN0eWxlIGNhbGxiYWNrIChubyB0aHJvdyBmb3IgY29udHJvbCBmbG93KS5cbiAqXG4gKiAtIGB7IG9rOiB0cnVlIH1gOiBzdWNjZXNzIChub3QgbGlzdGVkIGluIGBiYXRjaEl0ZW1GYWlsdXJlc2ApXG4gKiAtIGB7IG9rOiBmYWxzZSB9YDogZmFpbHVyZTsgaWYgYG9uUmVjb3JkRXJyb3JgIGlzIHNldCwgaXQgcmVjZWl2ZXMgYW4gYEVycm9yYCB3aG9zZVxuICogICBtZXNzYWdlIGluY2x1ZGVzIHRoZSByZXNvbHZlZCBgaXRlbUlkZW50aWZpZXJgLCBhbmQgd2hvc2UgYGNhdXNlYCBpcyBgeyBpdGVtSWRlbnRpZmllciB9YFxuICogLSB0aHJvd24gZXJyb3JzOiBzdGlsbCB0cmVhdGVkIGFzIGZhaWx1cmVzOyBgb25SZWNvcmRFcnJvcmAgcmVjZWl2ZXMgdGhlIHRocm93biB2YWx1ZSB3aGVuIHNldFxuICpcbiAqIEBwYXJhbSBldmVudCBUaGUgU1FTIExhbWJkYSBldmVudC5cbiAqIEBwYXJhbSBwcm9jZXNzUmVjb3JkIFBlci1yZWNvcmQgaGFuZGxlciByZXR1cm5pbmcgYHsgb2s6IHRydWUgfWAgb3IgYHsgb2s6IGZhbHNlIH1gLlxuICogQHBhcmFtIG9wdGlvbnMgT3B0aW9uYWwgY29uY3VycmVuY3ksIGVycm9yIGhvb2ssIGFuZCBtZXNzYWdlIGlkIG1hcHBpbmcuXG4gKiBAcmV0dXJucyBBbiB7QGxpbmsgU1FTQmF0Y2hSZXNwb25zZX0gbGlzdGluZyBvbmx5IGZhaWxlZCBgaXRlbUlkZW50aWZpZXJgcy5cbiAqIEB0aHJvd3Mge1JhbmdlRXJyb3J9IFdoZW4gYG9wdGlvbnMuY29uY3VycmVuY3lgIGlzIGxlc3MgdGhhbiAxLlxuICogQHRocm93cyB7VHlwZUVycm9yfSBXaGVuIGBvcHRpb25zLmNvbmN1cnJlbmN5YCBpcyBub3QgYSBmaW5pdGUgaW50ZWdlci5cbiAqL1xuZXhwb3J0IGNvbnN0IHByb2Nlc3NQYXJ0aWFsQmF0Y2hXaXRoUmVzdWx0ID0gYXN5bmMgKFxuICBldmVudDogU1FTRXZlbnQsXG4gIHByb2Nlc3NSZWNvcmQ6IChyZWNvcmQ6IFNRU1JlY29yZCkgPT4gUHJvbWlzZTxQcm9jZXNzUmVjb3JkUmVzdWx0PixcbiAgb3B0aW9ucz86IFByb2Nlc3NQYXJ0aWFsQmF0Y2hPcHRpb25zLFxuKTogUHJvbWlzZTxTUVNCYXRjaFJlc3BvbnNlPiA9PiB7XG4gIGNvbnN0IGNvbmN1cnJlbmN5ID0gcmVzb2x2ZUNvbmN1cnJlbmN5KG9wdGlvbnM/LmNvbmN1cnJlbmN5KTtcbiAgY29uc3QgYmF0Y2hJdGVtRmFpbHVyZXM6IHsgaXRlbUlkZW50aWZpZXI6IHN0cmluZyB9W10gPSBbXTtcblxuICAvKipcbiAgICogUHJvY2Vzc2VzIG9uZSByZWNvcmQ7IG1hcHMgdGhyb3dzIGFuZCBgeyBvazogZmFsc2UgfWAgdG8gYmF0Y2ggaXRlbSBmYWlsdXJlcy5cbiAgICpcbiAgICogQHBhcmFtIHJlY29yZCBUaGUgU1FTIHJlY29yZCB0byBwcm9jZXNzLlxuICAgKi9cbiAgY29uc3QgaGFuZGxlID0gYXN5bmMgKHJlY29yZDogU1FTUmVjb3JkKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgY29uc3QgaWQgPSBvcHRpb25zPy5tYXBNZXNzYWdlSWQ/LihyZWNvcmQpID8/IHJlY29yZC5tZXNzYWdlSWQ7XG4gICAgbGV0IHJlc3VsdDogUHJvY2Vzc1JlY29yZFJlc3VsdDtcbiAgICB0cnkge1xuICAgICAgcmVzdWx0ID0gYXdhaXQgcHJvY2Vzc1JlY29yZChyZWNvcmQpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAob3B0aW9ucz8ub25SZWNvcmRFcnJvcikge1xuICAgICAgICBvcHRpb25zLm9uUmVjb3JkRXJyb3IocmVjb3JkLCBlcnJvcik7XG4gICAgICB9XG4gICAgICBiYXRjaEl0ZW1GYWlsdXJlcy5wdXNoKGl0ZW1GYWlsdXJlKGlkKSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChyZXN1bHQub2spIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKG9wdGlvbnM/Lm9uUmVjb3JkRXJyb3IpIHtcbiAgICAgIGNvbnN0IGVycm9yID0gbmV3IEVycm9yKGBwcm9jZXNzUmVjb3JkIHJldHVybmVkIHsgb2s6IGZhbHNlIH0gKGl0ZW1JZGVudGlmaWVyPSR7aWR9KWApO1xuICAgICAgT2JqZWN0LmFzc2lnbihlcnJvciwgeyBjYXVzZTogeyBpdGVtSWRlbnRpZmllcjogaWQgfSB9KTtcbiAgICAgIG9wdGlvbnMub25SZWNvcmRFcnJvcihyZWNvcmQsIGVycm9yKTtcbiAgICB9XG4gICAgYmF0Y2hJdGVtRmFpbHVyZXMucHVzaChpdGVtRmFpbHVyZShpZCkpO1xuICB9O1xuXG4gIGlmIChjb25jdXJyZW5jeSA8PSAxKSB7XG4gICAgZm9yIChjb25zdCByZWNvcmQgb2YgZXZlbnQuUmVjb3Jkcykge1xuICAgICAgYXdhaXQgaGFuZGxlKHJlY29yZCk7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIGF3YWl0IHJ1bldpdGhDb25jdXJyZW5jeShldmVudC5SZWNvcmRzLCBjb25jdXJyZW5jeSwgaGFuZGxlKTtcbiAgfVxuXG4gIHJldHVybiB7IGJhdGNoSXRlbUZhaWx1cmVzIH07XG59O1xuIl19
@@ -0,0 +1,14 @@
1
+ import type { SQSBatchResponse, SQSEvent, SQSRecord } from 'aws-lambda';
2
+ import type { ProcessPartialBatchOptions } from '../types';
3
+ /**
4
+ * Runs `processRecord` for each SQS record. Thrown errors are mapped to
5
+ * `batchItemFailures`; successful records are not listed.
6
+ *
7
+ * @param event The SQS Lambda event.
8
+ * @param processRecord Per-record handler. Throw to mark only that record as failed.
9
+ * @param options Optional concurrency, error hook, and message id mapping.
10
+ * @returns An {@link SQSBatchResponse} listing only failed `itemIdentifier`s.
11
+ * @throws {RangeError} When `options.concurrency` is less than 1.
12
+ * @throws {TypeError} When `options.concurrency` is not a finite integer.
13
+ */
14
+ export declare const processPartialBatch: (event: SQSEvent, processRecord: (record: SQSRecord) => Promise<void>, options?: ProcessPartialBatchOptions) => Promise<SQSBatchResponse>;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processPartialBatch = void 0;
4
+ const concurrency_1 = require("./concurrency");
5
+ const item_failure_1 = require("./item-failure");
6
+ /**
7
+ * Runs `processRecord` for each SQS record. Thrown errors are mapped to
8
+ * `batchItemFailures`; successful records are not listed.
9
+ *
10
+ * @param event The SQS Lambda event.
11
+ * @param processRecord Per-record handler. Throw to mark only that record as failed.
12
+ * @param options Optional concurrency, error hook, and message id mapping.
13
+ * @returns An {@link SQSBatchResponse} listing only failed `itemIdentifier`s.
14
+ * @throws {RangeError} When `options.concurrency` is less than 1.
15
+ * @throws {TypeError} When `options.concurrency` is not a finite integer.
16
+ */
17
+ const processPartialBatch = async (event, processRecord, options) => {
18
+ const concurrency = (0, concurrency_1.resolveConcurrency)(options?.concurrency);
19
+ const batchItemFailures = [];
20
+ /**
21
+ * Processes one record and records a batch item failure on error.
22
+ *
23
+ * @param record The SQS record to process.
24
+ */
25
+ const handle = async (record) => {
26
+ const id = options?.mapMessageId?.(record) ?? record.messageId;
27
+ try {
28
+ await processRecord(record);
29
+ }
30
+ catch (error) {
31
+ if (options?.onRecordError) {
32
+ options.onRecordError(record, error);
33
+ }
34
+ batchItemFailures.push((0, item_failure_1.itemFailure)(id));
35
+ }
36
+ };
37
+ if (concurrency <= 1) {
38
+ for (const record of event.Records) {
39
+ await handle(record);
40
+ }
41
+ }
42
+ else {
43
+ await (0, concurrency_1.runWithConcurrency)(event.Records, concurrency, handle);
44
+ }
45
+ return { batchItemFailures };
46
+ };
47
+ exports.processPartialBatch = processPartialBatch;
48
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvY2Vzcy1wYXJ0aWFsLWJhdGNoLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Byb2Nlc3Nvci9wcm9jZXNzLXBhcnRpYWwtYmF0Y2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBRUEsK0NBQXVFO0FBQ3ZFLGlEQUE2QztBQUU3Qzs7Ozs7Ozs7OztHQVVHO0FBQ0ksTUFBTSxtQkFBbUIsR0FBRyxLQUFLLEVBQ3RDLEtBQWUsRUFDZixhQUFtRCxFQUNuRCxPQUFvQyxFQUNULEVBQUU7SUFDN0IsTUFBTSxXQUFXLEdBQUcsSUFBQSxnQ0FBa0IsRUFBQyxPQUFPLEVBQUUsV0FBVyxDQUFDLENBQUM7SUFDN0QsTUFBTSxpQkFBaUIsR0FBaUMsRUFBRSxDQUFDO0lBRTNEOzs7O09BSUc7SUFDSCxNQUFNLE1BQU0sR0FBRyxLQUFLLEVBQUUsTUFBaUIsRUFBaUIsRUFBRTtRQUN4RCxNQUFNLEVBQUUsR0FBRyxPQUFPLEVBQUUsWUFBWSxFQUFFLENBQUMsTUFBTSxDQUFDLElBQUksTUFBTSxDQUFDLFNBQVMsQ0FBQztRQUMvRCxJQUFJLENBQUM7WUFDSCxNQUFNLGFBQWEsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUM5QixDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLElBQUksT0FBTyxFQUFFLGFBQWEsRUFBRSxDQUFDO2dCQUMzQixPQUFPLENBQUMsYUFBYSxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsQ0FBQztZQUN2QyxDQUFDO1lBQ0QsaUJBQWlCLENBQUMsSUFBSSxDQUFDLElBQUEsMEJBQVcsRUFBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQzFDLENBQUM7SUFDSCxDQUFDLENBQUM7SUFFRixJQUFJLFdBQVcsSUFBSSxDQUFDLEVBQUUsQ0FBQztRQUNyQixLQUFLLE1BQU0sTUFBTSxJQUFJLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNuQyxNQUFNLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUN2QixDQUFDO0lBQ0gsQ0FBQztTQUFNLENBQUM7UUFDTixNQUFNLElBQUEsZ0NBQWtCLEVBQUMsS0FBSyxDQUFDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDL0QsQ0FBQztJQUVELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxDQUFDO0FBQy9CLENBQUMsQ0FBQztBQWxDVyxRQUFBLG1CQUFtQix1QkFrQzlCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBTUVNCYXRjaFJlc3BvbnNlLCBTUVNFdmVudCwgU1FTUmVjb3JkIH0gZnJvbSAnYXdzLWxhbWJkYSc7XG5pbXBvcnQgdHlwZSB7IFByb2Nlc3NQYXJ0aWFsQmF0Y2hPcHRpb25zIH0gZnJvbSAnLi4vdHlwZXMnO1xuaW1wb3J0IHsgcmVzb2x2ZUNvbmN1cnJlbmN5LCBydW5XaXRoQ29uY3VycmVuY3kgfSBmcm9tICcuL2NvbmN1cnJlbmN5JztcbmltcG9ydCB7IGl0ZW1GYWlsdXJlIH0gZnJvbSAnLi9pdGVtLWZhaWx1cmUnO1xuXG4vKipcbiAqIFJ1bnMgYHByb2Nlc3NSZWNvcmRgIGZvciBlYWNoIFNRUyByZWNvcmQuIFRocm93biBlcnJvcnMgYXJlIG1hcHBlZCB0b1xuICogYGJhdGNoSXRlbUZhaWx1cmVzYDsgc3VjY2Vzc2Z1bCByZWNvcmRzIGFyZSBub3QgbGlzdGVkLlxuICpcbiAqIEBwYXJhbSBldmVudCBUaGUgU1FTIExhbWJkYSBldmVudC5cbiAqIEBwYXJhbSBwcm9jZXNzUmVjb3JkIFBlci1yZWNvcmQgaGFuZGxlci4gVGhyb3cgdG8gbWFyayBvbmx5IHRoYXQgcmVjb3JkIGFzIGZhaWxlZC5cbiAqIEBwYXJhbSBvcHRpb25zIE9wdGlvbmFsIGNvbmN1cnJlbmN5LCBlcnJvciBob29rLCBhbmQgbWVzc2FnZSBpZCBtYXBwaW5nLlxuICogQHJldHVybnMgQW4ge0BsaW5rIFNRU0JhdGNoUmVzcG9uc2V9IGxpc3Rpbmcgb25seSBmYWlsZWQgYGl0ZW1JZGVudGlmaWVyYHMuXG4gKiBAdGhyb3dzIHtSYW5nZUVycm9yfSBXaGVuIGBvcHRpb25zLmNvbmN1cnJlbmN5YCBpcyBsZXNzIHRoYW4gMS5cbiAqIEB0aHJvd3Mge1R5cGVFcnJvcn0gV2hlbiBgb3B0aW9ucy5jb25jdXJyZW5jeWAgaXMgbm90IGEgZmluaXRlIGludGVnZXIuXG4gKi9cbmV4cG9ydCBjb25zdCBwcm9jZXNzUGFydGlhbEJhdGNoID0gYXN5bmMgKFxuICBldmVudDogU1FTRXZlbnQsXG4gIHByb2Nlc3NSZWNvcmQ6IChyZWNvcmQ6IFNRU1JlY29yZCkgPT4gUHJvbWlzZTx2b2lkPixcbiAgb3B0aW9ucz86IFByb2Nlc3NQYXJ0aWFsQmF0Y2hPcHRpb25zLFxuKTogUHJvbWlzZTxTUVNCYXRjaFJlc3BvbnNlPiA9PiB7XG4gIGNvbnN0IGNvbmN1cnJlbmN5ID0gcmVzb2x2ZUNvbmN1cnJlbmN5KG9wdGlvbnM/LmNvbmN1cnJlbmN5KTtcbiAgY29uc3QgYmF0Y2hJdGVtRmFpbHVyZXM6IHsgaXRlbUlkZW50aWZpZXI6IHN0cmluZyB9W10gPSBbXTtcblxuICAvKipcbiAgICogUHJvY2Vzc2VzIG9uZSByZWNvcmQgYW5kIHJlY29yZHMgYSBiYXRjaCBpdGVtIGZhaWx1cmUgb24gZXJyb3IuXG4gICAqXG4gICAqIEBwYXJhbSByZWNvcmQgVGhlIFNRUyByZWNvcmQgdG8gcHJvY2Vzcy5cbiAgICovXG4gIGNvbnN0IGhhbmRsZSA9IGFzeW5jIChyZWNvcmQ6IFNRU1JlY29yZCk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgIGNvbnN0IGlkID0gb3B0aW9ucz8ubWFwTWVzc2FnZUlkPy4ocmVjb3JkKSA/PyByZWNvcmQubWVzc2FnZUlkO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBwcm9jZXNzUmVjb3JkKHJlY29yZCk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChvcHRpb25zPy5vblJlY29yZEVycm9yKSB7XG4gICAgICAgIG9wdGlvbnMub25SZWNvcmRFcnJvcihyZWNvcmQsIGVycm9yKTtcbiAgICAgIH1cbiAgICAgIGJhdGNoSXRlbUZhaWx1cmVzLnB1c2goaXRlbUZhaWx1cmUoaWQpKTtcbiAgICB9XG4gIH07XG5cbiAgaWYgKGNvbmN1cnJlbmN5IDw9IDEpIHtcbiAgICBmb3IgKGNvbnN0IHJlY29yZCBvZiBldmVudC5SZWNvcmRzKSB7XG4gICAgICBhd2FpdCBoYW5kbGUocmVjb3JkKTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgYXdhaXQgcnVuV2l0aENvbmN1cnJlbmN5KGV2ZW50LlJlY29yZHMsIGNvbmN1cnJlbmN5LCBoYW5kbGUpO1xuICB9XG5cbiAgcmV0dXJuIHsgYmF0Y2hJdGVtRmFpbHVyZXMgfTtcbn07XG4iXX0=
@@ -0,0 +1,52 @@
1
+ import type { SQSRecord } from 'aws-lambda';
2
+ /**
3
+ * Result of a per-record handler used by {@link processPartialBatchWithResult}.
4
+ *
5
+ * - `{ ok: true }`: success (not listed in `batchItemFailures`)
6
+ * - `{ ok: false }`: failure (listed in `batchItemFailures`)
7
+ */
8
+ export type ProcessRecordResult = {
9
+ ok: true;
10
+ } | {
11
+ ok: false;
12
+ };
13
+ /**
14
+ * Options for {@link processPartialBatch} and {@link processPartialBatchWithResult}.
15
+ */
16
+ export interface ProcessPartialBatchOptions {
17
+ /**
18
+ * Maximum number of records processed in parallel.
19
+ *
20
+ * - `1`: sequential processing
21
+ * - `> 1`: bounded concurrency pool (order of {@link SQSBatchResponse.batchItemFailures} is not guaranteed)
22
+ *
23
+ * @default 1
24
+ * @throws {RangeError} When `concurrency` is less than 1.
25
+ * @throws {TypeError} When `concurrency` is not a finite integer.
26
+ */
27
+ readonly concurrency?: number;
28
+ /**
29
+ * Called when a record is treated as failed (thrown error or `{ ok: false }`).
30
+ * Use for logging or metrics; the library does not write to `console` by default.
31
+ *
32
+ * For `{ ok: false }` from {@link processPartialBatchWithResult}, `error` is an `Error`
33
+ * whose message includes the resolved `itemIdentifier`, and whose `cause` is
34
+ * `{ itemIdentifier }` for structured logging.
35
+ *
36
+ * Do not log `record.body` as-is — it may contain secrets or personal data.
37
+ * Prefer identifiers such as `record.messageId` (or your `mapMessageId` result)
38
+ * and a sanitized error summary.
39
+ *
40
+ * @param record The failed SQS record.
41
+ * @param error The thrown value, or a synthesized `Error` when `{ ok: false }` was returned.
42
+ */
43
+ readonly onRecordError?: (record: SQSRecord, error: unknown) => void;
44
+ /**
45
+ * Returns the `itemIdentifier` reported in `batchItemFailures` for a record.
46
+ * Defaults to `record.messageId` when omitted.
47
+ *
48
+ * @param record The SQS record being processed.
49
+ * @returns The identifier sent back to Lambda in `batchItemFailures`.
50
+ */
51
+ readonly mapMessageId?: (record: SQSRecord) => string;
52
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdHlwZXMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgU1FTUmVjb3JkIH0gZnJvbSAnYXdzLWxhbWJkYSc7XG5cbi8qKlxuICogUmVzdWx0IG9mIGEgcGVyLXJlY29yZCBoYW5kbGVyIHVzZWQgYnkge0BsaW5rIHByb2Nlc3NQYXJ0aWFsQmF0Y2hXaXRoUmVzdWx0fS5cbiAqXG4gKiAtIGB7IG9rOiB0cnVlIH1gOiBzdWNjZXNzIChub3QgbGlzdGVkIGluIGBiYXRjaEl0ZW1GYWlsdXJlc2ApXG4gKiAtIGB7IG9rOiBmYWxzZSB9YDogZmFpbHVyZSAobGlzdGVkIGluIGBiYXRjaEl0ZW1GYWlsdXJlc2ApXG4gKi9cbmV4cG9ydCB0eXBlIFByb2Nlc3NSZWNvcmRSZXN1bHQgPSB7IG9rOiB0cnVlIH0gfCB7IG9rOiBmYWxzZSB9O1xuXG4vKipcbiAqIE9wdGlvbnMgZm9yIHtAbGluayBwcm9jZXNzUGFydGlhbEJhdGNofSBhbmQge0BsaW5rIHByb2Nlc3NQYXJ0aWFsQmF0Y2hXaXRoUmVzdWx0fS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBQcm9jZXNzUGFydGlhbEJhdGNoT3B0aW9ucyB7XG4gIC8qKlxuICAgKiBNYXhpbXVtIG51bWJlciBvZiByZWNvcmRzIHByb2Nlc3NlZCBpbiBwYXJhbGxlbC5cbiAgICpcbiAgICogLSBgMWA6IHNlcXVlbnRpYWwgcHJvY2Vzc2luZ1xuICAgKiAtIGA+IDFgOiBib3VuZGVkIGNvbmN1cnJlbmN5IHBvb2wgKG9yZGVyIG9mIHtAbGluayBTUVNCYXRjaFJlc3BvbnNlLmJhdGNoSXRlbUZhaWx1cmVzfSBpcyBub3QgZ3VhcmFudGVlZClcbiAgICpcbiAgICogQGRlZmF1bHQgMVxuICAgKiBAdGhyb3dzIHtSYW5nZUVycm9yfSBXaGVuIGBjb25jdXJyZW5jeWAgaXMgbGVzcyB0aGFuIDEuXG4gICAqIEB0aHJvd3Mge1R5cGVFcnJvcn0gV2hlbiBgY29uY3VycmVuY3lgIGlzIG5vdCBhIGZpbml0ZSBpbnRlZ2VyLlxuICAgKi9cbiAgcmVhZG9ubHkgY29uY3VycmVuY3k/OiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIENhbGxlZCB3aGVuIGEgcmVjb3JkIGlzIHRyZWF0ZWQgYXMgZmFpbGVkICh0aHJvd24gZXJyb3Igb3IgYHsgb2s6IGZhbHNlIH1gKS5cbiAgICogVXNlIGZvciBsb2dnaW5nIG9yIG1ldHJpY3M7IHRoZSBsaWJyYXJ5IGRvZXMgbm90IHdyaXRlIHRvIGBjb25zb2xlYCBieSBkZWZhdWx0LlxuICAgKlxuICAgKiBGb3IgYHsgb2s6IGZhbHNlIH1gIGZyb20ge0BsaW5rIHByb2Nlc3NQYXJ0aWFsQmF0Y2hXaXRoUmVzdWx0fSwgYGVycm9yYCBpcyBhbiBgRXJyb3JgXG4gICAqIHdob3NlIG1lc3NhZ2UgaW5jbHVkZXMgdGhlIHJlc29sdmVkIGBpdGVtSWRlbnRpZmllcmAsIGFuZCB3aG9zZSBgY2F1c2VgIGlzXG4gICAqIGB7IGl0ZW1JZGVudGlmaWVyIH1gIGZvciBzdHJ1Y3R1cmVkIGxvZ2dpbmcuXG4gICAqXG4gICAqIERvIG5vdCBsb2cgYHJlY29yZC5ib2R5YCBhcy1pcyDigJQgaXQgbWF5IGNvbnRhaW4gc2VjcmV0cyBvciBwZXJzb25hbCBkYXRhLlxuICAgKiBQcmVmZXIgaWRlbnRpZmllcnMgc3VjaCBhcyBgcmVjb3JkLm1lc3NhZ2VJZGAgKG9yIHlvdXIgYG1hcE1lc3NhZ2VJZGAgcmVzdWx0KVxuICAgKiBhbmQgYSBzYW5pdGl6ZWQgZXJyb3Igc3VtbWFyeS5cbiAgICpcbiAgICogQHBhcmFtIHJlY29yZCBUaGUgZmFpbGVkIFNRUyByZWNvcmQuXG4gICAqIEBwYXJhbSBlcnJvciBUaGUgdGhyb3duIHZhbHVlLCBvciBhIHN5bnRoZXNpemVkIGBFcnJvcmAgd2hlbiBgeyBvazogZmFsc2UgfWAgd2FzIHJldHVybmVkLlxuICAgKi9cbiAgcmVhZG9ubHkgb25SZWNvcmRFcnJvcj86IChyZWNvcmQ6IFNRU1JlY29yZCwgZXJyb3I6IHVua25vd24pID0+IHZvaWQ7XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIGBpdGVtSWRlbnRpZmllcmAgcmVwb3J0ZWQgaW4gYGJhdGNoSXRlbUZhaWx1cmVzYCBmb3IgYSByZWNvcmQuXG4gICAqIERlZmF1bHRzIHRvIGByZWNvcmQubWVzc2FnZUlkYCB3aGVuIG9taXR0ZWQuXG4gICAqXG4gICAqIEBwYXJhbSByZWNvcmQgVGhlIFNRUyByZWNvcmQgYmVpbmcgcHJvY2Vzc2VkLlxuICAgKiBAcmV0dXJucyBUaGUgaWRlbnRpZmllciBzZW50IGJhY2sgdG8gTGFtYmRhIGluIGBiYXRjaEl0ZW1GYWlsdXJlc2AuXG4gICAqL1xuICByZWFkb25seSBtYXBNZXNzYWdlSWQ/OiAocmVjb3JkOiBTUVNSZWNvcmQpID0+IHN0cmluZztcbn1cbiJdfQ==
package/package.json CHANGED
@@ -42,9 +42,9 @@
42
42
  "eslint": "^9",
43
43
  "eslint-import-resolver-typescript": "^4.4.5",
44
44
  "eslint-plugin-import": "^2.32.0",
45
- "jest": "^30.5.0",
45
+ "jest": "^30.5.1",
46
46
  "jest-junit": "^17",
47
- "projen": "^0.103.11",
47
+ "projen": "^0.103.20",
48
48
  "ts-jest": "^29.4.12",
49
49
  "ts-node": "^10.9.2",
50
50
  "typescript": "6.0.x"
@@ -73,7 +73,7 @@
73
73
  "publishConfig": {
74
74
  "access": "public"
75
75
  },
76
- "version": "0.3.7",
76
+ "version": "0.4.1",
77
77
  "jest": {
78
78
  "coverageProvider": "v8",
79
79
  "testMatch": [