@message-queue-toolkit/core 25.0.0 → 25.1.0
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.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/payload-store/JsonStreamStringifySerializer.d.ts +1 -1
- package/dist/types/MessageQueueTypes.d.ts +2 -1
- package/dist/types/queueOptionsTypes.d.ts +113 -0
- package/dist/types/queueOptionsTypes.js +16 -1
- package/dist/types/queueOptionsTypes.js.map +1 -1
- package/dist/utils/startupResourcePollingUtils.d.ts +59 -0
- package/dist/utils/startupResourcePollingUtils.js +199 -0
- package/dist/utils/startupResourcePollingUtils.js.map +1 -0
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -29,5 +29,6 @@ export { isProduction, reloadConfig } from './utils/envUtils.ts';
|
|
|
29
29
|
export { isShallowSubset, objectMatches } from './utils/matchUtils.ts';
|
|
30
30
|
export { type ParseMessageResult, parseMessage } from './utils/parseUtils.ts';
|
|
31
31
|
export { objectToBuffer } from './utils/queueUtils.ts';
|
|
32
|
+
export { isStartupResourcePollingEnabled, type StartupResourcePollingCheckResult, StartupResourcePollingTimeoutError, type WaitForResourceOptions, waitForResource, } from './utils/startupResourcePollingUtils.ts';
|
|
32
33
|
export { toDatePreprocessor } from './utils/toDateProcessor.ts';
|
|
33
34
|
export { waitAndRetry } from './utils/waitUtils.ts';
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ export { isProduction, reloadConfig } from "./utils/envUtils.js";
|
|
|
24
24
|
export { isShallowSubset, objectMatches } from "./utils/matchUtils.js";
|
|
25
25
|
export { parseMessage } from "./utils/parseUtils.js";
|
|
26
26
|
export { objectToBuffer } from "./utils/queueUtils.js";
|
|
27
|
+
export { isStartupResourcePollingEnabled, StartupResourcePollingTimeoutError, waitForResource, } from "./utils/startupResourcePollingUtils.js";
|
|
27
28
|
export { toDatePreprocessor } from "./utils/toDateProcessor.js";
|
|
28
29
|
export { waitAndRetry } from "./utils/waitUtils.js";
|
|
29
30
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AACxE,OAAO,EACL,cAAc,EACd,yBAAyB,EACzB,sBAAsB,GACvB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAA;AAC3E,cAAc,8BAA8B,CAAA;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACzD,cAAc,wBAAwB,CAAA;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAC7D,cAAc,oDAAoD,CAAA;AAClE,OAAO,EAGL,0BAA0B,EAG1B,kBAAkB,GAEnB,MAAM,sDAAsD,CAAA;AAC7D,cAAc,kCAAkC,CAAA;AAChD,cAAc,8BAA8B,CAAA;AAC5C,OAAO,EACL,gCAAgC,EAChC,wCAAwC,EAExC,kBAAkB,GAEnB,MAAM,mDAAmD,CAAA;AAS1D,OAAO,EACL,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,sCAAsC,CAAA;AAC7C,cAAc,sCAAsC,CAAA;AACpD,OAAO,EACL,oBAAoB,GAGrB,MAAM,kCAAkC,CAAA;AAWzC,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,2BAA2B,GAE5B,MAAM,8BAA8B,CAAA;AACrC,OAAO,EACL,gBAAgB,EAChB,UAAU,EAGV,iBAAiB,EAEjB,iBAAiB,GAClB,MAAM,wBAAwB,CAAA;AAM/B,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAA;AAM3E,OAAO,EACL,4BAA4B,EAC5B,0BAA0B,EAC1B,uBAAuB,EACvB,6BAA6B,EAC7B,kBAAkB,GACnB,MAAM,iCAAiC,CAAA;AAUxC,cAAc,8BAA8B,CAAA;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAChE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACtE,OAAO,EAA2B,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AACxE,OAAO,EACL,cAAc,EACd,yBAAyB,EACzB,sBAAsB,GACvB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAA;AAC3E,cAAc,8BAA8B,CAAA;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACzD,cAAc,wBAAwB,CAAA;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAC7D,cAAc,oDAAoD,CAAA;AAClE,OAAO,EAGL,0BAA0B,EAG1B,kBAAkB,GAEnB,MAAM,sDAAsD,CAAA;AAC7D,cAAc,kCAAkC,CAAA;AAChD,cAAc,8BAA8B,CAAA;AAC5C,OAAO,EACL,gCAAgC,EAChC,wCAAwC,EAExC,kBAAkB,GAEnB,MAAM,mDAAmD,CAAA;AAS1D,OAAO,EACL,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,sCAAsC,CAAA;AAC7C,cAAc,sCAAsC,CAAA;AACpD,OAAO,EACL,oBAAoB,GAGrB,MAAM,kCAAkC,CAAA;AAWzC,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,2BAA2B,GAE5B,MAAM,8BAA8B,CAAA;AACrC,OAAO,EACL,gBAAgB,EAChB,UAAU,EAGV,iBAAiB,EAEjB,iBAAiB,GAClB,MAAM,wBAAwB,CAAA;AAM/B,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAA;AAM3E,OAAO,EACL,4BAA4B,EAC5B,0BAA0B,EAC1B,uBAAuB,EACvB,6BAA6B,EAC7B,kBAAkB,GACnB,MAAM,iCAAiC,CAAA;AAUxC,cAAc,8BAA8B,CAAA;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAChE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACtE,OAAO,EAA2B,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EACL,+BAA+B,EAE/B,kCAAkC,EAElC,eAAe,GAChB,MAAM,wCAAwC,CAAA;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA"}
|
|
@@ -5,7 +5,7 @@ export declare class JsonStreamStringifySerializer implements PayloadSerializer
|
|
|
5
5
|
private readonly temporaryFilePathResolver;
|
|
6
6
|
constructor(temporaryFilePathResolver?: TemporaryFilePathResolver);
|
|
7
7
|
serialize(payload: unknown): Promise<{
|
|
8
|
-
value: import("stream").Readable;
|
|
8
|
+
value: import("node:stream").Readable;
|
|
9
9
|
size: number;
|
|
10
10
|
destroy: () => Promise<void>;
|
|
11
11
|
}>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CommonLogger, TransactionObservabilityManager } from '@lokalise/node-core';
|
|
1
|
+
import type { CommonLogger, ErrorReporter, TransactionObservabilityManager } from '@lokalise/node-core';
|
|
2
2
|
import type { ZodSchema } from 'zod/v4';
|
|
3
3
|
import type { PublicHandlerSpy } from '../queues/HandlerSpy.ts';
|
|
4
4
|
export interface QueueConsumer {
|
|
@@ -28,5 +28,6 @@ export interface AsyncPublisher<MessagePayloadType extends object, MessageOption
|
|
|
28
28
|
export type { TransactionObservabilityManager };
|
|
29
29
|
export type ExtraParams = {
|
|
30
30
|
logger?: CommonLogger;
|
|
31
|
+
errorReporter?: ErrorReporter;
|
|
31
32
|
};
|
|
32
33
|
export type SchemaMap<SupportedMessageTypes extends string> = Record<SupportedMessageTypes, ZodSchema<any>>;
|
|
@@ -112,6 +112,119 @@ export type DeletionConfig = {
|
|
|
112
112
|
waitForConfirmation?: boolean;
|
|
113
113
|
forceDeleteInProduction?: boolean;
|
|
114
114
|
};
|
|
115
|
+
/**
|
|
116
|
+
* Symbol to indicate no timeout - polling will continue indefinitely.
|
|
117
|
+
* Use this for dev/staging environments where you want to wait indefinitely for resources.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* {
|
|
121
|
+
* locatorConfig: {
|
|
122
|
+
* queueUrl: '...',
|
|
123
|
+
* startupResourcePolling: {
|
|
124
|
+
* enabled: true,
|
|
125
|
+
* timeoutMs: NO_TIMEOUT,
|
|
126
|
+
* }
|
|
127
|
+
* }
|
|
128
|
+
* }
|
|
129
|
+
*/
|
|
130
|
+
export declare const NO_TIMEOUT: unique symbol;
|
|
131
|
+
/**
|
|
132
|
+
* Configuration for startup resource polling mode when resources may not exist at startup.
|
|
133
|
+
*
|
|
134
|
+
* This is useful in scenarios where services have cross-dependencies:
|
|
135
|
+
* - Service A needs to subscribe to Service B's topic
|
|
136
|
+
* - Service B needs to subscribe to Service A's topic
|
|
137
|
+
* - Neither can deploy first without the other's topic existing
|
|
138
|
+
*
|
|
139
|
+
* When `startupResourcePolling` is provided with `enabled: true` inside `locatorConfig`,
|
|
140
|
+
* the consumer will poll for the topic/queue to become available instead of failing immediately.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* // Enable with 5 minute timeout
|
|
144
|
+
* {
|
|
145
|
+
* locatorConfig: {
|
|
146
|
+
* queueUrl: '...',
|
|
147
|
+
* startupResourcePolling: {
|
|
148
|
+
* enabled: true,
|
|
149
|
+
* timeoutMs: 5 * 60 * 1000,
|
|
150
|
+
* }
|
|
151
|
+
* }
|
|
152
|
+
* }
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* // Poll indefinitely (useful for dev/staging environments)
|
|
156
|
+
* {
|
|
157
|
+
* locatorConfig: {
|
|
158
|
+
* queueUrl: '...',
|
|
159
|
+
* startupResourcePolling: {
|
|
160
|
+
* enabled: true,
|
|
161
|
+
* timeoutMs: NO_TIMEOUT,
|
|
162
|
+
* }
|
|
163
|
+
* }
|
|
164
|
+
* }
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* // Custom timeout and interval
|
|
168
|
+
* {
|
|
169
|
+
* locatorConfig: {
|
|
170
|
+
* queueUrl: '...',
|
|
171
|
+
* startupResourcePolling: {
|
|
172
|
+
* enabled: true,
|
|
173
|
+
* timeoutMs: 10 * 60 * 1000, // 10 minutes
|
|
174
|
+
* pollingIntervalMs: 10000,
|
|
175
|
+
* }
|
|
176
|
+
* }
|
|
177
|
+
* }
|
|
178
|
+
*/
|
|
179
|
+
export type StartupResourcePollingConfig = {
|
|
180
|
+
/**
|
|
181
|
+
* Controls whether polling is enabled.
|
|
182
|
+
* Must be set to true to enable polling.
|
|
183
|
+
*/
|
|
184
|
+
enabled?: boolean;
|
|
185
|
+
/**
|
|
186
|
+
* Maximum time in milliseconds to wait for the resource to become available.
|
|
187
|
+
* Use `NO_TIMEOUT` to disable timeout and poll indefinitely.
|
|
188
|
+
*/
|
|
189
|
+
timeoutMs: number | typeof NO_TIMEOUT;
|
|
190
|
+
/**
|
|
191
|
+
* Interval in milliseconds between polling attempts.
|
|
192
|
+
* Default: 5000 (5 seconds)
|
|
193
|
+
*/
|
|
194
|
+
pollingIntervalMs?: number;
|
|
195
|
+
/**
|
|
196
|
+
* Whether to throw an error when timeout is reached.
|
|
197
|
+
* - `true` (default): Throws `StartupResourcePollingTimeoutError` when timeout is reached
|
|
198
|
+
* - `false`: Reports the error via errorReporter, resets the timeout counter, and continues polling
|
|
199
|
+
*
|
|
200
|
+
* Use `false` when you want to be notified about prolonged unavailability but don't want to fail.
|
|
201
|
+
* Default: true
|
|
202
|
+
*/
|
|
203
|
+
throwOnTimeout?: boolean;
|
|
204
|
+
/**
|
|
205
|
+
* Whether to run polling in non-blocking mode.
|
|
206
|
+
* - `false` (default): init() waits for the resource to become available before resolving
|
|
207
|
+
* - `true`: If resource is not immediately available, init() resolves immediately and
|
|
208
|
+
* polling continues in the background. When the resource becomes available,
|
|
209
|
+
* the `onResourceAvailable` callback is invoked.
|
|
210
|
+
*
|
|
211
|
+
* Use `true` when you want the service to start quickly without waiting for dependencies.
|
|
212
|
+
* Default: false
|
|
213
|
+
*/
|
|
214
|
+
nonBlocking?: boolean;
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* Base type for queue locator configurations that includes startup resource polling.
|
|
218
|
+
* Protocol-specific locator types should extend this.
|
|
219
|
+
*/
|
|
220
|
+
export type BaseQueueLocatorType = {
|
|
221
|
+
/**
|
|
222
|
+
* Configuration for startup resource polling mode.
|
|
223
|
+
* When enabled, the consumer will poll for the resource to become available
|
|
224
|
+
* instead of failing immediately.
|
|
225
|
+
*/
|
|
226
|
+
startupResourcePolling?: StartupResourcePollingConfig;
|
|
227
|
+
};
|
|
115
228
|
type NewQueueOptions<CreationConfigType extends CommonCreationConfigType> = {
|
|
116
229
|
creationConfig?: CreationConfigType;
|
|
117
230
|
};
|
|
@@ -1,2 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Symbol to indicate no timeout - polling will continue indefinitely.
|
|
3
|
+
* Use this for dev/staging environments where you want to wait indefinitely for resources.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* {
|
|
7
|
+
* locatorConfig: {
|
|
8
|
+
* queueUrl: '...',
|
|
9
|
+
* startupResourcePolling: {
|
|
10
|
+
* enabled: true,
|
|
11
|
+
* timeoutMs: NO_TIMEOUT,
|
|
12
|
+
* }
|
|
13
|
+
* }
|
|
14
|
+
* }
|
|
15
|
+
*/
|
|
16
|
+
export const NO_TIMEOUT = Symbol('NO_TIMEOUT');
|
|
2
17
|
//# sourceMappingURL=queueOptionsTypes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queueOptionsTypes.js","sourceRoot":"","sources":["../../lib/types/queueOptionsTypes.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"queueOptionsTypes.js","sourceRoot":"","sources":["../../lib/types/queueOptionsTypes.ts"],"names":[],"mappings":"AAoIA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,YAAY,CAAC,CAAA"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { CommonLogger, ErrorReporter } from '@lokalise/node-core';
|
|
2
|
+
import { type StartupResourcePollingConfig } from '../types/queueOptionsTypes.ts';
|
|
3
|
+
export type StartupResourcePollingCheckResult<T> = {
|
|
4
|
+
isAvailable: true;
|
|
5
|
+
result: T;
|
|
6
|
+
} | {
|
|
7
|
+
isAvailable: false;
|
|
8
|
+
};
|
|
9
|
+
export type WaitForResourceOptions<T> = {
|
|
10
|
+
/**
|
|
11
|
+
* Startup resource polling configuration
|
|
12
|
+
*/
|
|
13
|
+
config: StartupResourcePollingConfig;
|
|
14
|
+
/**
|
|
15
|
+
* Function that checks if the resource is available.
|
|
16
|
+
* Should return { isAvailable: true, result: T } when resource exists,
|
|
17
|
+
* or { isAvailable: false } when resource doesn't exist.
|
|
18
|
+
* Should throw on unexpected errors.
|
|
19
|
+
*/
|
|
20
|
+
checkFn: () => Promise<StartupResourcePollingCheckResult<T>>;
|
|
21
|
+
/**
|
|
22
|
+
* Human-readable name of the resource for logging
|
|
23
|
+
*/
|
|
24
|
+
resourceName: string;
|
|
25
|
+
/**
|
|
26
|
+
* Logger instance for progress logging
|
|
27
|
+
*/
|
|
28
|
+
logger?: CommonLogger;
|
|
29
|
+
/**
|
|
30
|
+
* Error reporter for reporting timeout errors when throwOnTimeout is false.
|
|
31
|
+
* Required when config.throwOnTimeout is false.
|
|
32
|
+
*/
|
|
33
|
+
errorReporter?: ErrorReporter;
|
|
34
|
+
/**
|
|
35
|
+
* Callback invoked when resource becomes available in non-blocking mode.
|
|
36
|
+
* Only used when config.nonBlocking is true and the resource was not immediately available.
|
|
37
|
+
*/
|
|
38
|
+
onResourceAvailable?: (result: T) => void;
|
|
39
|
+
};
|
|
40
|
+
export declare class StartupResourcePollingTimeoutError extends Error {
|
|
41
|
+
readonly resourceName: string;
|
|
42
|
+
readonly timeoutMs: number;
|
|
43
|
+
constructor(resourceName: string, timeoutMs: number);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Waits for a resource to become available by polling.
|
|
47
|
+
* This is used for startup resource polling mode where resources may not exist at startup.
|
|
48
|
+
*
|
|
49
|
+
* @param options - Configuration and check function
|
|
50
|
+
* @returns The result from the check function when resource becomes available,
|
|
51
|
+
* or undefined if nonBlocking is true and resource was not immediately available
|
|
52
|
+
* @throws StartupResourcePollingTimeoutError if timeout is reached and throwOnTimeout is true (default)
|
|
53
|
+
*/
|
|
54
|
+
export declare function waitForResource<T>(options: WaitForResourceOptions<T>): Promise<T | undefined>;
|
|
55
|
+
/**
|
|
56
|
+
* Helper to check if startup resource polling is enabled.
|
|
57
|
+
* Returns true only when config is provided and enabled is explicitly true.
|
|
58
|
+
*/
|
|
59
|
+
export declare function isStartupResourcePollingEnabled(config?: StartupResourcePollingConfig): config is StartupResourcePollingConfig;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { setTimeout } from 'node:timers/promises';
|
|
2
|
+
import { NO_TIMEOUT } from "../types/queueOptionsTypes.js";
|
|
3
|
+
const DEFAULT_POLLING_INTERVAL_MS = 5000;
|
|
4
|
+
export class StartupResourcePollingTimeoutError extends Error {
|
|
5
|
+
resourceName;
|
|
6
|
+
timeoutMs;
|
|
7
|
+
constructor(resourceName, timeoutMs) {
|
|
8
|
+
super(`Timeout waiting for resource "${resourceName}" to become available after ${timeoutMs}ms. ` +
|
|
9
|
+
'The resource may not exist or there may be a configuration issue.');
|
|
10
|
+
this.name = 'StartupResourcePollingTimeoutError';
|
|
11
|
+
this.resourceName = resourceName;
|
|
12
|
+
this.timeoutMs = timeoutMs;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function checkTimeoutExceeded(hasTimeout, timeoutMs, startTime, resourceName, attemptCount, logger) {
|
|
16
|
+
if (!hasTimeout)
|
|
17
|
+
return { exceeded: false };
|
|
18
|
+
const elapsedMs = Date.now() - startTime;
|
|
19
|
+
if (elapsedMs >= timeoutMs) {
|
|
20
|
+
logger?.error({
|
|
21
|
+
message: `Timeout waiting for resource "${resourceName}" to become available`,
|
|
22
|
+
resourceName,
|
|
23
|
+
timeoutMs,
|
|
24
|
+
attemptCount,
|
|
25
|
+
elapsedMs,
|
|
26
|
+
});
|
|
27
|
+
return {
|
|
28
|
+
exceeded: true,
|
|
29
|
+
error: new StartupResourcePollingTimeoutError(resourceName, timeoutMs),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return { exceeded: false };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Handles timeout condition - either throws or reports and returns new timeout count.
|
|
36
|
+
* @returns Updated timeout count after handling
|
|
37
|
+
*/
|
|
38
|
+
function handleTimeout(params) {
|
|
39
|
+
const { timeoutResult, throwOnTimeout, resourceName, timeoutMs, attemptCount, timeoutCount, logger, errorReporter, } = params;
|
|
40
|
+
if (throwOnTimeout) {
|
|
41
|
+
throw timeoutResult.error;
|
|
42
|
+
}
|
|
43
|
+
// Report error and reset timeout counter
|
|
44
|
+
const newTimeoutCount = timeoutCount + 1;
|
|
45
|
+
errorReporter?.report({
|
|
46
|
+
error: timeoutResult.error,
|
|
47
|
+
context: {
|
|
48
|
+
resourceName,
|
|
49
|
+
timeoutMs,
|
|
50
|
+
attemptCount,
|
|
51
|
+
timeoutCount: newTimeoutCount,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
logger?.warn({
|
|
55
|
+
message: `Timeout waiting for resource "${resourceName}", resetting timeout counter and continuing`,
|
|
56
|
+
resourceName,
|
|
57
|
+
timeoutMs,
|
|
58
|
+
attemptCount,
|
|
59
|
+
timeoutCount: newTimeoutCount,
|
|
60
|
+
});
|
|
61
|
+
return newTimeoutCount;
|
|
62
|
+
}
|
|
63
|
+
function logResourceAvailable(resourceName, attemptCount, startTime, logger) {
|
|
64
|
+
const elapsedMs = Date.now() - startTime;
|
|
65
|
+
logger?.info({
|
|
66
|
+
message: `Resource "${resourceName}" is now available`,
|
|
67
|
+
resourceName,
|
|
68
|
+
attemptCount,
|
|
69
|
+
elapsedMs,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Internal polling loop that waits for a resource to become available.
|
|
74
|
+
* Separated from main function to support non-blocking mode.
|
|
75
|
+
*/
|
|
76
|
+
async function pollForResource(options, pollingIntervalMs, hasTimeout, timeoutMs, throwOnTimeout, initialAttemptCount) {
|
|
77
|
+
const { checkFn, resourceName, logger, errorReporter } = options;
|
|
78
|
+
let startTime = Date.now();
|
|
79
|
+
let attemptCount = initialAttemptCount;
|
|
80
|
+
let timeoutCount = 0;
|
|
81
|
+
while (true) {
|
|
82
|
+
attemptCount++;
|
|
83
|
+
const timeoutResult = checkTimeoutExceeded(hasTimeout, timeoutMs, startTime, resourceName, attemptCount, logger);
|
|
84
|
+
if (timeoutResult.exceeded) {
|
|
85
|
+
timeoutCount = handleTimeout({
|
|
86
|
+
timeoutResult,
|
|
87
|
+
throwOnTimeout,
|
|
88
|
+
resourceName,
|
|
89
|
+
timeoutMs,
|
|
90
|
+
attemptCount,
|
|
91
|
+
timeoutCount,
|
|
92
|
+
logger,
|
|
93
|
+
errorReporter,
|
|
94
|
+
});
|
|
95
|
+
startTime = Date.now();
|
|
96
|
+
}
|
|
97
|
+
const result = await checkFn().catch((error) => {
|
|
98
|
+
// Unexpected error during check - log and rethrow
|
|
99
|
+
logger?.error({
|
|
100
|
+
message: `Error checking resource availability for "${resourceName}"`,
|
|
101
|
+
resourceName,
|
|
102
|
+
error,
|
|
103
|
+
attemptCount,
|
|
104
|
+
});
|
|
105
|
+
throw error;
|
|
106
|
+
});
|
|
107
|
+
if (result.isAvailable) {
|
|
108
|
+
logResourceAvailable(resourceName, attemptCount, startTime, logger);
|
|
109
|
+
return result.result;
|
|
110
|
+
}
|
|
111
|
+
// Resource not available yet, log and wait
|
|
112
|
+
if (attemptCount === 1 || attemptCount % 12 === 0) {
|
|
113
|
+
// Log on first attempt and then every minute (assuming 5s interval)
|
|
114
|
+
const elapsedMs = Date.now() - startTime;
|
|
115
|
+
logger?.debug({
|
|
116
|
+
message: `Resource "${resourceName}" not available yet, will retry`,
|
|
117
|
+
resourceName,
|
|
118
|
+
attemptCount,
|
|
119
|
+
elapsedMs,
|
|
120
|
+
nextRetryInMs: pollingIntervalMs,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
// Wait before next attempt
|
|
124
|
+
await setTimeout(pollingIntervalMs);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Waits for a resource to become available by polling.
|
|
129
|
+
* This is used for startup resource polling mode where resources may not exist at startup.
|
|
130
|
+
*
|
|
131
|
+
* @param options - Configuration and check function
|
|
132
|
+
* @returns The result from the check function when resource becomes available,
|
|
133
|
+
* or undefined if nonBlocking is true and resource was not immediately available
|
|
134
|
+
* @throws StartupResourcePollingTimeoutError if timeout is reached and throwOnTimeout is true (default)
|
|
135
|
+
*/
|
|
136
|
+
export async function waitForResource(options) {
|
|
137
|
+
const { config, checkFn, resourceName, logger, onResourceAvailable } = options;
|
|
138
|
+
const pollingIntervalMs = config.pollingIntervalMs ?? DEFAULT_POLLING_INTERVAL_MS;
|
|
139
|
+
const hasTimeout = config.timeoutMs !== NO_TIMEOUT;
|
|
140
|
+
const timeoutMs = hasTimeout ? config.timeoutMs : 0;
|
|
141
|
+
const throwOnTimeout = config.throwOnTimeout !== false;
|
|
142
|
+
const nonBlocking = config.nonBlocking === true;
|
|
143
|
+
logger?.info({
|
|
144
|
+
message: `Waiting for resource "${resourceName}" to become available`,
|
|
145
|
+
resourceName,
|
|
146
|
+
pollingIntervalMs,
|
|
147
|
+
timeoutMs: hasTimeout ? timeoutMs : 'NO_TIMEOUT',
|
|
148
|
+
throwOnTimeout,
|
|
149
|
+
nonBlocking,
|
|
150
|
+
});
|
|
151
|
+
// First check - always done synchronously
|
|
152
|
+
const initialResult = await checkFn().catch((error) => {
|
|
153
|
+
logger?.error({
|
|
154
|
+
message: `Error checking resource availability for "${resourceName}"`,
|
|
155
|
+
resourceName,
|
|
156
|
+
error,
|
|
157
|
+
attemptCount: 1,
|
|
158
|
+
});
|
|
159
|
+
throw error;
|
|
160
|
+
});
|
|
161
|
+
if (initialResult.isAvailable) {
|
|
162
|
+
logResourceAvailable(resourceName, 1, Date.now(), logger);
|
|
163
|
+
return initialResult.result;
|
|
164
|
+
}
|
|
165
|
+
// Resource not available on first check
|
|
166
|
+
if (nonBlocking) {
|
|
167
|
+
// Start background polling and return immediately
|
|
168
|
+
logger?.info({
|
|
169
|
+
message: `Resource "${resourceName}" not immediately available, starting background polling`,
|
|
170
|
+
resourceName,
|
|
171
|
+
pollingIntervalMs,
|
|
172
|
+
});
|
|
173
|
+
// Fire and forget - start polling in background
|
|
174
|
+
setTimeout(pollingIntervalMs).then(() => {
|
|
175
|
+
pollForResource(options, pollingIntervalMs, hasTimeout, timeoutMs, throwOnTimeout, 1)
|
|
176
|
+
.then((result) => {
|
|
177
|
+
onResourceAvailable?.(result);
|
|
178
|
+
})
|
|
179
|
+
.catch((error) => {
|
|
180
|
+
logger?.error({
|
|
181
|
+
message: `Background polling for resource "${resourceName}" failed`,
|
|
182
|
+
resourceName,
|
|
183
|
+
error,
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
// Blocking mode - continue polling and wait for result
|
|
190
|
+
return pollForResource(options, pollingIntervalMs, hasTimeout, timeoutMs, throwOnTimeout, 1);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Helper to check if startup resource polling is enabled.
|
|
194
|
+
* Returns true only when config is provided and enabled is explicitly true.
|
|
195
|
+
*/
|
|
196
|
+
export function isStartupResourcePollingEnabled(config) {
|
|
197
|
+
return config?.enabled === true;
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=startupResourcePollingUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startupResourcePollingUtils.js","sourceRoot":"","sources":["../../lib/utils/startupResourcePollingUtils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@message-queue-toolkit/core",
|
|
3
|
-
"version": "25.
|
|
3
|
+
"version": "25.1.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Useful utilities, interfaces and base classes for message queue handling. Supports AMQP and SQS with a common abstraction on top currently",
|
|
6
6
|
"maintainers": [
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"@lokalise/universal-ts-utils": "^4.5.1",
|
|
30
30
|
"@message-queue-toolkit/schemas": "^7.0.0",
|
|
31
31
|
"dot-prop": "^10.1.0",
|
|
32
|
-
"fast-equals": "^
|
|
32
|
+
"fast-equals": "^6.0.0",
|
|
33
33
|
"json-stream-stringify": "^3.1.6",
|
|
34
34
|
"tmp": "^0.2.3",
|
|
35
35
|
"toad-cache": "^3.7.0"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@biomejs/biome": "^2.3.8",
|
|
42
42
|
"@lokalise/biome-config": "^3.1.0",
|
|
43
43
|
"@lokalise/tsconfig": "^3.0.0",
|
|
44
|
-
"@types/node": "^
|
|
44
|
+
"@types/node": "^25.0.2",
|
|
45
45
|
"@types/tmp": "^0.2.6",
|
|
46
46
|
"@vitest/coverage-v8": "^3.2.4",
|
|
47
47
|
"awilix": "^12.0.5",
|