@owox/internal-helpers 0.13.0-next-20251119115009 → 0.13.0-next-20251124115326

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 CHANGED
@@ -3,5 +3,9 @@ export * from './environment/parse-mysql-ssl.js';
3
3
  export * from './logging/logger-factory.js';
4
4
  export * from './logging/types.js';
5
5
  export * from './utils/promiseWithTimeout.js';
6
+ export * from './utils/fetchWithBackoff.js';
7
+ export * from './utils/castError.js';
8
+ export * from './utils/isReadonlyQuery.js';
9
+ export * from './utils/runWithConcurrency.js';
6
10
  export * from './integrations/event-bus/index.js';
7
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oBAAoB,CAAC;AACnC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oBAAoB,CAAC;AACnC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC"}
package/dist/index.js CHANGED
@@ -3,4 +3,8 @@ export * from './environment/parse-mysql-ssl.js';
3
3
  export * from './logging/logger-factory.js';
4
4
  export * from './logging/types.js';
5
5
  export * from './utils/promiseWithTimeout.js';
6
+ export * from './utils/fetchWithBackoff.js';
7
+ export * from './utils/castError.js';
8
+ export * from './utils/isReadonlyQuery.js';
9
+ export * from './utils/runWithConcurrency.js';
6
10
  export * from './integrations/event-bus/index.js';
@@ -0,0 +1,2 @@
1
+ export declare function castError(error: unknown): Error;
2
+ //# sourceMappingURL=castError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"castError.d.ts","sourceRoot":"","sources":["../../src/utils/castError.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAQ/C"}
@@ -0,0 +1,12 @@
1
+ export function castError(error) {
2
+ if (error instanceof Error)
3
+ return error;
4
+ if (typeof error === 'string')
5
+ return new Error(error);
6
+ try {
7
+ return new Error(JSON.stringify(error));
8
+ }
9
+ catch {
10
+ return new Error(String(error));
11
+ }
12
+ }
@@ -0,0 +1,2 @@
1
+ export declare function fetchWithBackoff(url: string, options: RequestInit, maxRetries?: number, initialDelay?: number): Promise<Response>;
2
+ //# sourceMappingURL=fetchWithBackoff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetchWithBackoff.d.ts","sourceRoot":"","sources":["../../src/utils/fetchWithBackoff.ts"],"names":[],"mappings":"AAGA,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,WAAW,EACpB,UAAU,SAAI,EACd,YAAY,SAAM,GACjB,OAAO,CAAC,QAAQ,CAAC,CAwCnB"}
@@ -0,0 +1,36 @@
1
+ import { LoggerFactory } from '../logging/logger-factory.js';
2
+ import { castError } from './castError.js';
3
+ export async function fetchWithBackoff(url, options, maxRetries = 3, initialDelay = 300) {
4
+ const logger = LoggerFactory.createNamedLogger('fetchWithBackoff');
5
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
6
+ try {
7
+ const res = await fetch(url, options);
8
+ // Retry on 5xx or 429
9
+ if (res.status >= 500 || res.status === 429) {
10
+ const retryAfter = parseInt(res.headers.get('retry-after') ?? '0', 10);
11
+ const backoff = retryAfter > 0 ? retryAfter * 1000 : initialDelay * Math.pow(2, attempt - 1);
12
+ logger.debug(`[fetchWithBackoff] ${res.status} retrying in ${backoff}ms`);
13
+ await new Promise(r => setTimeout(r, backoff));
14
+ continue;
15
+ }
16
+ // Don't retry on 4xx other than 429
17
+ if (res.status >= 400)
18
+ return res;
19
+ return res;
20
+ }
21
+ catch (err) {
22
+ const msg = castError(err).message;
23
+ const isTransient = msg.includes('EPIPE') ||
24
+ msg.includes('ECONNRESET') ||
25
+ msg.includes('fetch failed') ||
26
+ msg.includes('timed out') ||
27
+ msg.includes('socket hang up');
28
+ if (!isTransient || attempt === maxRetries)
29
+ throw err;
30
+ const backoff = initialDelay * Math.pow(2, attempt - 1) + Math.random() * 100;
31
+ logger.debug(`[fetchWithBackoff] attempt ${attempt} failed (${msg}); retrying in ${Math.round(backoff)}ms`);
32
+ await new Promise(r => setTimeout(r, backoff));
33
+ }
34
+ }
35
+ throw new Error('fetchWithBackoff: exceeded max retries');
36
+ }
@@ -0,0 +1,2 @@
1
+ export declare function isReadonlyQuery(sql: string): boolean;
2
+ //# sourceMappingURL=isReadonlyQuery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isReadonlyQuery.d.ts","sourceRoot":"","sources":["../../src/utils/isReadonlyQuery.ts"],"names":[],"mappings":"AAAA,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAUpD"}
@@ -0,0 +1,10 @@
1
+ export function isReadonlyQuery(sql) {
2
+ const cleaned = sql
3
+ .trim()
4
+ .replace(/^--.*$/gm, '') // remove line comments
5
+ .replace(/\/\*[\s\S]*?\*\//g, '') // remove block comments
6
+ .trim()
7
+ .replace(/^\(+/, '') // strip leading parentheses
8
+ .trim();
9
+ return /^(WITH|SELECT)\b/i.test(cleaned);
10
+ }
@@ -0,0 +1,2 @@
1
+ export declare function runWithConcurrency<T, R>(items: readonly T[], limit: number, fn: (item: T, index: number) => Promise<R>): Promise<R[]>;
2
+ //# sourceMappingURL=runWithConcurrency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runWithConcurrency.d.ts","sourceRoot":"","sources":["../../src/utils/runWithConcurrency.ts"],"names":[],"mappings":"AAAA,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAC3C,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GACzC,OAAO,CAAC,CAAC,EAAE,CAAC,CAiBd"}
@@ -0,0 +1,14 @@
1
+ export async function runWithConcurrency(items, limit, fn) {
2
+ if (items.length === 0)
3
+ return [];
4
+ const size = Math.max(1, Math.min(limit, items.length));
5
+ const results = new Array(items.length);
6
+ for (let i = 0; i < items.length; i += size) {
7
+ const batch = items.slice(i, i + size);
8
+ const batchResults = await Promise.all(batch.map((item, j) => fn(item, i + j)));
9
+ batchResults.forEach((value, j) => {
10
+ results[i + j] = value;
11
+ });
12
+ }
13
+ return results;
14
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owox/internal-helpers",
3
- "version": "0.13.0-next-20251119115009",
3
+ "version": "0.13.0-next-20251124115326",
4
4
  "description": "Internal helpers used by core OWOX packages",
5
5
  "type": "module",
6
6
  "author": "OWOX",