@nmakarov/cli-toolkit 0.43.0 → 0.46.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/cli-runner.cjs +192 -9
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +192 -9
- package/dist/cli-runner.js.map +1 -1
- package/dist/db.cjs +163 -4
- package/dist/db.cjs.map +1 -1
- package/dist/db.js +163 -4
- package/dist/db.js.map +1 -1
- package/dist/filedatabase.cjs +26 -20
- package/dist/filedatabase.cjs.map +1 -1
- package/dist/filedatabase.js +26 -20
- package/dist/filedatabase.js.map +1 -1
- package/dist/http-client.cjs +1 -1
- package/dist/http-client.cjs.map +1 -1
- package/dist/http-client.js +1 -1
- package/dist/http-client.js.map +1 -1
- package/dist/index.cjs +232 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +232 -31
- package/dist/index.js.map +1 -1
- package/dist/tasks.cjs +43 -7
- package/dist/tasks.cjs.map +1 -1
- package/dist/tasks.js +43 -7
- package/dist/tasks.js.map +1 -1
- package/package.json +1 -1
package/dist/http-client.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/http-client/index.js","../src/errors.js","../src/http-client/errors.js","../src/http-client/retry.js"],"sourcesContent":["/**\n * HttpClient - Resilient HTTP Client with Retry Logic\n *\n * A production-ready HTTP client that:\n * - Wraps axios with enhanced error handling and retry logic\n * - Never throws exceptions - always returns unified response format\n * - Uses exponential backoff with jitter for retries\n * - Provides human-readable error classifications\n * - Supports comprehensive logging\n * - Handles all HTTP methods consistently\n */\n\nimport axios, { } from \"axios\";\n\n\n\n\n\n\n\n\nimport { HttpClientError } from \"../errors.js\";\nimport { classifyError, getErrorDescription } from \"./errors.js\";\nimport { calculateRetryDelay, sleep, shouldRetryError, } from \"./retry.js\";\n\nexport { HttpClientError };\n\n/**\n * Resilient HTTP Client with automatic retry logic.\n * Use HttpClient.init(context, options) when running with init/context; constructor(config) for standalone.\n */\nexport class HttpClient {\n axiosInstance;\n config;\n logger;\n\n constructor(contextOrConfig = {}, config) {\n const hasContext = config !== undefined;\n const options = hasContext ? config : (contextOrConfig || {});\n const context = hasContext ? contextOrConfig : undefined;\n\n this.config = {\n ...options,\n logger: context?.logger ?? options.logger ?? console,\n };\n this.logger = this.config.logger;\n\n this.axiosInstance = axios.create({\n timeout: this.config.timeout,\n validateStatus: () => true,\n maxRedirects: this.config.maxRedirects,\n headers: { \"User-Agent\": this.config.userAgent },\n httpsAgent: this.config.validateSSL ? undefined : { rejectUnauthorized: false } ,\n });\n\n // Add response interceptor for logging (optional - only if debug enabled)\n this.axiosInstance.interceptors.response.use(\n (response) => response,\n (error) => {\n // Log network-level errors here if needed\n // (HTTP errors are handled in the request method)\n return Promise.reject(error);\n }\n );\n }\n\n /**\n * Static init - discovers params via getAllForModule(defs). Whatever is in options goes.\n */\n static init(context, options = {}) {\n const defs = {\n timeout: \"number default 30000\",\n retryCount: \"number default 3\",\n retryDelay: \"number default 1000\",\n maxRetryDelay: \"number default 30000\",\n retryJitter: \"number default 0.1\",\n userAgent: \"string default HttpClient/v1.0\",\n validateSSL: \"boolean default true\",\n maxRedirects: \"number default 5\",\n };\n const discovered = context?.params?.getAllForModule?.(defs) ?? {};\n const merged = { ...discovered, ...options, logger: options.logger ?? context?.logger };\n return new HttpClient(context, merged);\n }\n\n /**\n * Make an HTTP request with automatic retry logic\n * Never throws - always returns HttpClientResponse\n */\n async request(\n method,\n url,\n options = {}\n ) {\n const startTime = Date.now();\n\n // Merge request options with defaults\n const requestConfig = {\n method,\n url,\n timeout: options.timeout ?? this.config.timeout,\n headers: {\n \"User-Agent\": options.userAgent ?? this.config.userAgent,\n ...options.headers\n },\n params: options.params,\n data: options.data\n };\n\n const retryCount = options.retryCount ?? this.config.retryCount;\n const retryDelay = options.retryDelay ?? this.config.retryDelay;\n\n // Initialize retry context\n const _retryContext = null;\n let _lastError = null;\n\n // Attempt the request with retries\n for (let attempt = 1; attempt <= retryCount + 1; attempt++) {\n try {\n if (options.debug) {\n this.logger.debug?.(`[HttpClient] ${method} ${url} (attempt ${attempt}/${retryCount + 1})`);\n }\n\n const response = await this.axiosInstance.request(requestConfig);\n const duration = Date.now() - startTime;\n\n // Success! Return unified response format\n const customStatus = this.mapHttpStatusToCustomStatus(response.status);\n\n if (options.debug) {\n this.logger.debug?.(`[HttpClient] ${method} ${url} → ${response.status} ${customStatus} (${duration}ms)`);\n }\n\n return {\n status: customStatus,\n code: response.status,\n headers: response.headers ,\n data: response.data,\n duration,\n retryCount: attempt - 1,\n finalUrl: response.request?.res?.responseUrl || url\n };\n\n } catch (error) {\n _lastError = error;\n const duration = Date.now() - startTime;\n\n // Classify the error\n const classification = classifyError(error);\n const errorDescription = getErrorDescription(classification.type);\n\n // Log the error\n if (classification.retryable && attempt <= retryCount) {\n this.logger.warn?.(`[HttpClient] ${method} ${url} failed (${classification.type}): ${errorDescription}. Retrying in ${retryDelay}ms...`);\n } else if (!classification.retryable || attempt > retryCount) {\n this.logger.error?.(`[HttpClient] ${method} ${url} failed (${classification.type}): ${errorDescription}`);\n }\n\n // Check if we should retry\n if (attempt <= retryCount && shouldRetryError(classification)) {\n // Calculate delay and wait\n const delay = calculateRetryDelay(\n attempt,\n retryDelay,\n this.config.maxRetryDelay,\n this.config.retryJitter\n );\n\n if (options.debug) {\n this.logger.debug?.(`[HttpClient] Waiting ${delay}ms before retry ${attempt + 1}`);\n }\n\n await sleep(delay);\n continue;\n }\n\n // No more retries or not retryable - return error response\n return {\n status: classification.status,\n code: error.response?.status || null,\n error: classification.type,\n headers: error.response?.headers || null,\n data: error.response?.data || null,\n duration,\n retryCount: attempt - 1,\n finalUrl: url\n };\n }\n }\n\n // This should never be reached, but just in case\n return {\n status: \"unknown\",\n code: null,\n error: \"unknown\",\n headers: null,\n data: null,\n duration: Date.now() - startTime,\n retryCount: retryCount,\n finalUrl: url\n };\n }\n\n /**\n * GET request\n */\n async get(url, options = {}) {\n return this.request(\"GET\", url, options);\n }\n\n /**\n * POST request\n */\n async post(url, options = {}) {\n return this.request(\"POST\", url, options);\n }\n\n /**\n * PUT request\n */\n async put(url, options = {}) {\n return this.request(\"PUT\", url, options);\n }\n\n /**\n * DELETE request\n */\n async delete(url, options = {}) {\n return this.request(\"DELETE\", url, options);\n }\n\n /**\n * PATCH request\n */\n async patch(url, options = {}) {\n return this.request(\"PATCH\", url, options);\n }\n\n /**\n * HEAD request\n */\n async head(url, options = {}) {\n return this.request(\"HEAD\", url, options);\n }\n\n /**\n * OPTIONS request\n */\n async options(url, options = {}) {\n return this.request(\"OPTIONS\", url, options);\n }\n\n /**\n * Map HTTP status code to custom status\n */\n mapHttpStatusToCustomStatus(httpStatus) {\n if (httpStatus >= 200 && httpStatus < 300) {\n return \"success\";\n } else if (httpStatus === 401) {\n return \"authRequired\";\n } else if (httpStatus === 403) {\n return \"authFailed\";\n } else if (httpStatus >= 400 && httpStatus < 500) {\n return \"clientError\";\n } else if (httpStatus >= 500) {\n return \"serverError\";\n }\n return \"unknown\";\n }\n\n /**\n * Get current configuration (for debugging)\n */\n getConfig() {\n return { ...this.config };\n }\n}\n","/**\n * Error classes for the CLI toolkit\n */\n\nexport class FrameworkError extends Error {\n constructor(message) {\n super(message);\n this.name = \"FrameworkError\";\n }\n}\n\nexport class ParamError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"ParamError\";\n }\n}\n\nexport class InitError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"InitError\";\n }\n}\n\nexport class CriticalRequestError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"CriticalRequestError\";\n }\n}\n\nexport class ControlFlowError extends Error {\n constructor(message) {\n super(message);\n this.name = \"ControlFlowError\";\n }\n}\n\nexport class HttpClientError extends FrameworkError {\n constructor(message, cause) {\n super(message);this.cause = cause;;\n this.name = \"HttpClientError\";\n }\n}\n\nexport class FileDatabaseError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"FileDatabaseError\";\n }\n}\n\n","\n\n\n\n\n\n\n\n/**\n * Classify an error and determine retry behavior\n */\nexport function classifyError(error) {\n // Handle axios response errors (HTTP errors)\n if (error.response) {\n const { status } = error.response;\n\n switch (status) {\n case 400:\n return { type: \"badRequest\", retryable: false, isAuth: false, status: \"clientError\" };\n case 401:\n return { type: \"unauthorized\", retryable: false, isAuth: true, status: \"authRequired\" };\n case 403:\n return { type: \"forbidden\", retryable: false, isAuth: true, status: \"authFailed\" };\n case 404:\n return { type: \"notFound\", retryable: false, isAuth: false, status: \"clientError\" };\n case 405:\n return { type: \"methodNotAllowed\", retryable: false, isAuth: false, status: \"clientError\" };\n case 409:\n return { type: \"conflict\", retryable: false, isAuth: false, status: \"clientError\" };\n case 422:\n return { type: \"unprocessableEntity\", retryable: false, isAuth: false, status: \"clientError\" };\n case 429:\n return { type: \"tooManyRequests\", retryable: true, isAuth: false, status: \"clientError\" };\n case 500:\n return { type: \"internalServerError\", retryable: true, isAuth: false, status: \"serverError\" };\n case 502:\n return { type: \"badGateway\", retryable: true, isAuth: false, status: \"serverError\" };\n case 503:\n return { type: \"serviceUnavailable\", retryable: true, isAuth: false, status: \"serverError\" };\n case 504:\n return { type: \"gatewayTimeout\", retryable: true, isAuth: false, status: \"serverError\" };\n default:\n if (status >= 400 && status < 500) {\n return { type: \"clientError\" , retryable: false, isAuth: false, status: \"clientError\" };\n } else if (status >= 500) {\n return { type: \"serverError\" , retryable: true, isAuth: false, status: \"serverError\" };\n }\n break;\n }\n }\n\n // Handle network/connection errors (no response)\n if (error.code) {\n switch (error.code) {\n case \"ECONNREFUSED\":\n case \"ECONNRESET\":\n case \"EPIPE\":\n case \"ENOTFOUND\":\n case \"EHOSTUNREACH\":\n case \"ENETUNREACH\":\n return { type: \"connectionFailed\", retryable: true, isAuth: false, status: \"networkError\" };\n\n case \"ETIMEDOUT\":\n case \"ECONNABORTED\":\n case \"ESOCKETTIMEDOUT\":\n return { type: \"timeout\", retryable: true, isAuth: false, status: \"timeout\" };\n\n case \"EAUTH\":\n case \"EACCES\":\n return { type: \"unauthorized\", retryable: false, isAuth: true, status: \"authRequired\" };\n\n default:\n return { type: \"networkError\", retryable: true, isAuth: false, status: \"networkError\" };\n }\n }\n\n // Handle timeout errors\n if (error.message && (\n error.message.includes(\"timeout\") ||\n error.message.includes(\"TIMEOUT\") ||\n error.message.includes(\"aborted\")\n )) {\n return { type: \"timeout\", retryable: true, isAuth: false, status: \"timeout\" };\n }\n\n // Handle cancellation\n if (error.name === \"AbortError\" || error.message?.includes(\"cancelled\")) {\n return { type: \"requestCancelled\", retryable: false, isAuth: false, status: \"unknown\" };\n }\n\n // Default fallback\n return { type: \"unknown\", retryable: false, isAuth: false, status: \"unknown\" };\n}\n\n/**\n * Get a human-readable description for an error type\n */\nexport function getErrorDescription(errorType) {\n const descriptions = {\n connectionFailed: \"Failed to establish a connection to the server\",\n timeout: \"Request timed out before completing\",\n networkError: \"Network connection issue occurred\",\n badRequest: \"Request was malformed or invalid\",\n unauthorized: \"Authentication credentials are required\",\n forbidden: \"Access to the requested resource is forbidden\",\n notFound: \"The requested resource was not found\",\n methodNotAllowed: \"HTTP method not allowed for this resource\",\n conflict: \"Request conflicts with current server state\",\n unprocessableEntity: \"Request data could not be processed\",\n tooManyRequests: \"Too many requests sent in a short time\",\n internalServerError: \"Server encountered an internal error\",\n badGateway: \"Invalid response from upstream server\",\n serviceUnavailable: \"Server is temporarily unavailable\",\n gatewayTimeout: \"Upstream server timed out\",\n unknown: \"An unknown error occurred\",\n requestCancelled: \"Request was cancelled before completion\"\n };\n\n return descriptions[errorType] || \"An unknown error occurred\";\n}\n","\n\n\n\n\n\n\n\n/**\n * Calculate the next retry delay using exponential backoff with jitter\n *\n * Formula: delay = baseDelay * (2 ^ (attempt - 1)) + randomJitter\n *\n * Jitter prevents the \"thundering herd\" problem where multiple failed requests\n * all retry at the exact same time, overwhelming the server.\n */\nexport function calculateRetryDelay(\n attempt,\n baseDelay,\n maxDelay,\n jitterFactor = 0.1\n) {\n // Exponential backoff: baseDelay * (2 ^ (attempt - 1))\n const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);\n\n // Cap at maximum delay\n const cappedDelay = Math.min(exponentialDelay, maxDelay);\n\n // Add jitter: random variation up to jitterFactor of the delay\n const jitter = cappedDelay * jitterFactor * Math.random();\n\n return Math.floor(cappedDelay + jitter);\n}\n\n/**\n * Sleep for the specified number of milliseconds\n */\nexport function sleep(ms) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Determine if an error should be retried based on classification\n */\nexport function shouldRetryError(classification) {\n // Never retry auth errors (they won't succeed with the same credentials)\n if (classification.isAuth) {\n return false;\n }\n\n // Retry if the error is classified as retryable\n return classification.retryable;\n}\n\n/**\n * Create a retry context for tracking retry attempts\n */\nexport function createRetryContext(\n maxAttempts,\n baseDelay,\n maxDelay,\n jitterFactor\n) {\n return {\n attempt: 1,\n maxAttempts,\n lastError: new Error(\"Initial attempt\"),\n totalDelay: 0,\n nextDelay: calculateRetryDelay(1, baseDelay, maxDelay, jitterFactor)\n };\n}\n\n/**\n * Update retry context for the next attempt\n */\nexport function updateRetryContext(\n context,\n lastError,\n baseDelay,\n maxDelay,\n jitterFactor\n) {\n const nextAttempt = context.attempt + 1;\n const nextDelay = calculateRetryDelay(nextAttempt, baseDelay, maxDelay, jitterFactor);\n\n return {\n attempt: nextAttempt,\n maxAttempts: context.maxAttempts,\n lastError,\n totalDelay: context.totalDelay + context.nextDelay,\n nextDelay\n };\n}\n"],"mappings":"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","names":["axios"]}
|
|
1
|
+
{"version":3,"sources":["../src/http-client/index.js","../src/errors.js","../src/http-client/errors.js","../src/http-client/retry.js"],"sourcesContent":["/**\n * HttpClient - Resilient HTTP Client with Retry Logic\n *\n * A production-ready HTTP client that:\n * - Wraps axios with enhanced error handling and retry logic\n * - Never throws exceptions - always returns unified response format\n * - Uses exponential backoff with jitter for retries\n * - Provides human-readable error classifications\n * - Supports comprehensive logging\n * - Handles all HTTP methods consistently\n */\n\nimport axios, { } from \"axios\";\n\n\n\n\n\n\n\n\nimport { HttpClientError } from \"../errors.js\";\nimport { classifyError, getErrorDescription } from \"./errors.js\";\nimport { calculateRetryDelay, sleep, shouldRetryError, } from \"./retry.js\";\n\nexport { HttpClientError };\n\n/**\n * Resilient HTTP Client with automatic retry logic.\n * Use HttpClient.init(context, options) when running with init/context; constructor(config) for standalone.\n */\nexport class HttpClient {\n axiosInstance;\n config;\n logger;\n\n constructor(contextOrConfig = {}, config) {\n const hasContext = config !== undefined;\n const options = hasContext ? config : (contextOrConfig || {});\n const context = hasContext ? contextOrConfig : undefined;\n\n this.config = {\n ...options,\n logger: context?.logger ?? options.logger ?? console,\n };\n this.logger = this.config.logger;\n\n this.axiosInstance = axios.create({\n timeout: this.config.timeout,\n validateStatus: () => true,\n maxRedirects: this.config.maxRedirects,\n headers: { \"User-Agent\": this.config.userAgent },\n httpsAgent: this.config.validateSSL ? undefined : { rejectUnauthorized: false } ,\n });\n\n // Add response interceptor for logging (optional - only if debug enabled)\n this.axiosInstance.interceptors.response.use(\n (response) => response,\n (error) => {\n // Log network-level errors here if needed\n // (HTTP errors are handled in the request method)\n return Promise.reject(error);\n }\n );\n }\n\n /**\n * Static init - discovers params via getAllForModule(defs). Whatever is in options goes.\n */\n static init(context, options = {}) {\n const defs = {\n timeout: \"number default 30000\",\n retryCount: \"number default 3\",\n retryDelay: \"number default 1000\",\n maxRetryDelay: \"number default 30000\",\n retryJitter: \"number default 0.1\",\n userAgent: \"string default HttpClient/v1.0\",\n validateSSL: \"boolean default true\",\n maxRedirects: \"number default 5\",\n };\n const discovered = context?.params?.getAllForModule?.(defs) ?? {};\n const merged = { ...discovered, ...options, logger: options.logger ?? context?.logger };\n return new HttpClient(context, merged);\n }\n\n /**\n * Make an HTTP request with automatic retry logic\n * Never throws - always returns HttpClientResponse\n */\n async request(\n method,\n url,\n options = {}\n ) {\n const startTime = Date.now();\n\n // Merge request options with defaults\n const requestConfig = {\n method,\n url,\n timeout: options.timeout ?? this.config.timeout,\n headers: {\n \"User-Agent\": options.userAgent ?? this.config.userAgent,\n ...options.headers\n },\n params: options.params,\n data: options.data\n };\n\n const retryCount = options.retryCount ?? this.config.retryCount;\n const retryDelay = options.retryDelay ?? this.config.retryDelay;\n\n // Initialize retry context\n const _retryContext = null;\n let _lastError = null;\n\n // Attempt the request with retries\n for (let attempt = 1; attempt <= retryCount + 1; attempt++) {\n try {\n if (options.debug) {\n this.logger.debug?.(`[HttpClient] ${method} ${url} (attempt ${attempt}/${retryCount + 1})`);\n }\n\n const response = await this.axiosInstance.request(requestConfig);\n const duration = Date.now() - startTime;\n\n // Success! Return unified response format\n const customStatus = this.mapHttpStatusToCustomStatus(response.status);\n\n if (options.debug) {\n this.logger.debug?.(`[HttpClient] ${method} ${url} → ${response.status} ${customStatus} (${duration}ms)`);\n }\n\n return {\n status: customStatus,\n code: response.status,\n headers: response.headers ,\n data: response.data,\n duration,\n retryCount: attempt - 1,\n finalUrl: response.request?.res?.responseUrl || url\n };\n\n } catch (error) {\n _lastError = error;\n const duration = Date.now() - startTime;\n\n // Classify the error\n const classification = classifyError(error);\n const errorDescription = getErrorDescription(classification.type);\n\n // Log the error\n if (classification.retryable && attempt <= retryCount) {\n this.logger.warn?.(`[HttpClient] ${method} ${url} failed (${classification.type}): ${errorDescription}. Retrying in ${retryDelay}ms...`);\n } else if (!classification.retryable || attempt > retryCount) {\n this.logger.error?.(`[HttpClient] ${method} ${url} failed (${classification.type}): ${errorDescription}`);\n }\n\n // Check if we should retry\n if (attempt <= retryCount && shouldRetryError(classification)) {\n // Calculate delay and wait\n const delay = calculateRetryDelay(\n attempt,\n retryDelay,\n this.config.maxRetryDelay,\n this.config.retryJitter\n );\n\n if (options.debug) {\n this.logger.debug?.(`[HttpClient] Waiting ${delay}ms before retry ${attempt + 1}`);\n }\n\n await sleep(delay);\n continue;\n }\n\n // No more retries or not retryable - return error response\n return {\n status: classification.status,\n code: error.response?.status || null,\n error: classification.type,\n headers: error.response?.headers || null,\n data: error.response?.data || null,\n duration,\n retryCount: attempt - 1,\n finalUrl: url\n };\n }\n }\n\n // This should never be reached, but just in case\n return {\n status: \"unknown\",\n code: null,\n error: \"unknown\",\n headers: null,\n data: null,\n duration: Date.now() - startTime,\n retryCount: retryCount,\n finalUrl: url\n };\n }\n\n /**\n * GET request\n */\n async get(url, options = {}) {\n return this.request(\"GET\", url, options);\n }\n\n /**\n * POST request\n */\n async post(url, options = {}) {\n return this.request(\"POST\", url, options);\n }\n\n /**\n * PUT request\n */\n async put(url, options = {}) {\n return this.request(\"PUT\", url, options);\n }\n\n /**\n * DELETE request\n */\n async delete(url, options = {}) {\n return this.request(\"DELETE\", url, options);\n }\n\n /**\n * PATCH request\n */\n async patch(url, options = {}) {\n return this.request(\"PATCH\", url, options);\n }\n\n /**\n * HEAD request\n */\n async head(url, options = {}) {\n return this.request(\"HEAD\", url, options);\n }\n\n /**\n * OPTIONS request\n */\n async options(url, options = {}) {\n return this.request(\"OPTIONS\", url, options);\n }\n\n /**\n * Map HTTP status code to custom status\n */\n mapHttpStatusToCustomStatus(httpStatus) {\n if (httpStatus >= 200 && httpStatus < 300) {\n return \"success\";\n } else if (httpStatus === 401) {\n return \"authRequired\";\n } else if (httpStatus === 403) {\n return \"authFailed\";\n } else if (httpStatus >= 400 && httpStatus < 500) {\n return \"clientError\";\n } else if (httpStatus >= 500) {\n return \"serverError\";\n }\n return \"unknown\";\n }\n\n /**\n * Get current configuration (for debugging)\n */\n getConfig() {\n return { ...this.config };\n }\n}\n","/**\n * Error classes for the CLI toolkit\n */\n\nexport class FrameworkError extends Error {\n constructor(message) {\n super(message);\n this.name = \"FrameworkError\";\n }\n}\n\nexport class ParamError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"ParamError\";\n }\n}\n\nexport class InitError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"InitError\";\n }\n}\n\nexport class CriticalRequestError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"CriticalRequestError\";\n }\n}\n\nexport class ControlFlowError extends Error {\n constructor(message) {\n super(message);\n this.name = \"ControlFlowError\";\n }\n}\n\nexport class HttpClientError extends FrameworkError {\n constructor(message, cause) {\n super(message);this.cause = cause;;\n this.name = \"HttpClientError\";\n }\n}\n\nexport class FileDatabaseError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"FileDatabaseError\";\n }\n}\n\n","\n\n\n\n\n\n\n\n/**\n * Classify an error and determine retry behavior\n */\nexport function classifyError(error) {\n // Handle axios response errors (HTTP errors)\n if (error.response) {\n const { status } = error.response;\n\n switch (status) {\n case 400:\n return { type: \"badRequest\", retryable: false, isAuth: false, status: \"clientError\" };\n case 401:\n return { type: \"unauthorized\", retryable: false, isAuth: true, status: \"authRequired\" };\n case 403:\n return { type: \"forbidden\", retryable: false, isAuth: true, status: \"authFailed\" };\n case 404:\n return { type: \"notFound\", retryable: false, isAuth: false, status: \"clientError\" };\n case 405:\n return { type: \"methodNotAllowed\", retryable: false, isAuth: false, status: \"clientError\" };\n case 409:\n return { type: \"conflict\", retryable: false, isAuth: false, status: \"clientError\" };\n case 422:\n return { type: \"unprocessableEntity\", retryable: false, isAuth: false, status: \"clientError\" };\n case 429:\n return { type: \"tooManyRequests\", retryable: true, isAuth: false, status: \"clientError\" };\n case 500:\n return { type: \"internalServerError\", retryable: true, isAuth: false, status: \"serverError\" };\n case 502:\n return { type: \"badGateway\", retryable: true, isAuth: false, status: \"serverError\" };\n case 503:\n return { type: \"serviceUnavailable\", retryable: true, isAuth: false, status: \"serverError\" };\n case 504:\n return { type: \"gatewayTimeout\", retryable: true, isAuth: false, status: \"serverError\" };\n default:\n if (status >= 400 && status < 500) {\n return { type: \"clientError\" , retryable: false, isAuth: false, status: \"clientError\" };\n } else if (status >= 500) {\n return { type: \"serverError\" , retryable: true, isAuth: false, status: \"serverError\" };\n }\n break;\n }\n }\n\n // Handle network/connection errors (no response)\n if (error.code) {\n switch (error.code) {\n case \"ECONNREFUSED\":\n case \"ECONNRESET\":\n case \"EPIPE\":\n case \"ENOTFOUND\":\n case \"EHOSTUNREACH\":\n case \"ENETUNREACH\":\n return { type: \"connectionFailed\", retryable: true, isAuth: false, status: \"networkError\" };\n\n case \"ETIMEDOUT\":\n case \"ECONNABORTED\":\n case \"ESOCKETTIMEDOUT\":\n return { type: \"timeout\", retryable: true, isAuth: false, status: \"timeout\" };\n\n case \"EAUTH\":\n case \"EACCES\":\n return { type: \"unauthorized\", retryable: false, isAuth: true, status: \"authRequired\" };\n\n default:\n return { type: \"networkError\", retryable: true, isAuth: false, status: \"networkError\" };\n }\n }\n\n // Handle timeout errors\n if (error.message && (\n error.message.includes(\"timeout\") ||\n error.message.includes(\"TIMEOUT\") ||\n error.message.includes(\"aborted\")\n )) {\n return { type: \"timeout\", retryable: true, isAuth: false, status: \"timeout\" };\n }\n\n // Handle cancellation\n if (error.name === \"AbortError\" || error.message?.includes(\"cancelled\")) {\n return { type: \"requestCancelled\", retryable: false, isAuth: false, status: \"unknown\" };\n }\n\n // Default fallback — treat as transient (socket/TLS glitches often lack a code).\n return { type: \"unknown\", retryable: true, isAuth: false, status: \"unknown\" };\n}\n\n/**\n * Get a human-readable description for an error type\n */\nexport function getErrorDescription(errorType) {\n const descriptions = {\n connectionFailed: \"Failed to establish a connection to the server\",\n timeout: \"Request timed out before completing\",\n networkError: \"Network connection issue occurred\",\n badRequest: \"Request was malformed or invalid\",\n unauthorized: \"Authentication credentials are required\",\n forbidden: \"Access to the requested resource is forbidden\",\n notFound: \"The requested resource was not found\",\n methodNotAllowed: \"HTTP method not allowed for this resource\",\n conflict: \"Request conflicts with current server state\",\n unprocessableEntity: \"Request data could not be processed\",\n tooManyRequests: \"Too many requests sent in a short time\",\n internalServerError: \"Server encountered an internal error\",\n badGateway: \"Invalid response from upstream server\",\n serviceUnavailable: \"Server is temporarily unavailable\",\n gatewayTimeout: \"Upstream server timed out\",\n unknown: \"An unknown error occurred\",\n requestCancelled: \"Request was cancelled before completion\"\n };\n\n return descriptions[errorType] || \"An unknown error occurred\";\n}\n","\n\n\n\n\n\n\n\n/**\n * Calculate the next retry delay using exponential backoff with jitter\n *\n * Formula: delay = baseDelay * (2 ^ (attempt - 1)) + randomJitter\n *\n * Jitter prevents the \"thundering herd\" problem where multiple failed requests\n * all retry at the exact same time, overwhelming the server.\n */\nexport function calculateRetryDelay(\n attempt,\n baseDelay,\n maxDelay,\n jitterFactor = 0.1\n) {\n // Exponential backoff: baseDelay * (2 ^ (attempt - 1))\n const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);\n\n // Cap at maximum delay\n const cappedDelay = Math.min(exponentialDelay, maxDelay);\n\n // Add jitter: random variation up to jitterFactor of the delay\n const jitter = cappedDelay * jitterFactor * Math.random();\n\n return Math.floor(cappedDelay + jitter);\n}\n\n/**\n * Sleep for the specified number of milliseconds\n */\nexport function sleep(ms) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Determine if an error should be retried based on classification\n */\nexport function shouldRetryError(classification) {\n // Never retry auth errors (they won't succeed with the same credentials)\n if (classification.isAuth) {\n return false;\n }\n\n // Retry if the error is classified as retryable\n return classification.retryable;\n}\n\n/**\n * Create a retry context for tracking retry attempts\n */\nexport function createRetryContext(\n maxAttempts,\n baseDelay,\n maxDelay,\n jitterFactor\n) {\n return {\n attempt: 1,\n maxAttempts,\n lastError: new Error(\"Initial attempt\"),\n totalDelay: 0,\n nextDelay: calculateRetryDelay(1, baseDelay, maxDelay, jitterFactor)\n };\n}\n\n/**\n * Update retry context for the next attempt\n */\nexport function updateRetryContext(\n context,\n lastError,\n baseDelay,\n maxDelay,\n jitterFactor\n) {\n const nextAttempt = context.attempt + 1;\n const nextDelay = calculateRetryDelay(nextAttempt, baseDelay, maxDelay, jitterFactor);\n\n return {\n attempt: nextAttempt,\n maxAttempts: context.maxAttempts,\n lastError,\n totalDelay: context.totalDelay + context.nextDelay,\n nextDelay\n };\n}\n"],"mappings":"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","names":["axios"]}
|
package/dist/http-client.js
CHANGED
|
@@ -81,7 +81,7 @@ function classifyError(error) {
|
|
|
81
81
|
if (error.name === "AbortError" || error.message?.includes("cancelled")) {
|
|
82
82
|
return { type: "requestCancelled", retryable: false, isAuth: false, status: "unknown" };
|
|
83
83
|
}
|
|
84
|
-
return { type: "unknown", retryable:
|
|
84
|
+
return { type: "unknown", retryable: true, isAuth: false, status: "unknown" };
|
|
85
85
|
}
|
|
86
86
|
function getErrorDescription(errorType) {
|
|
87
87
|
const descriptions = {
|
package/dist/http-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/http-client/index.js","../src/errors.js","../src/http-client/errors.js","../src/http-client/retry.js"],"sourcesContent":["/**\n * HttpClient - Resilient HTTP Client with Retry Logic\n *\n * A production-ready HTTP client that:\n * - Wraps axios with enhanced error handling and retry logic\n * - Never throws exceptions - always returns unified response format\n * - Uses exponential backoff with jitter for retries\n * - Provides human-readable error classifications\n * - Supports comprehensive logging\n * - Handles all HTTP methods consistently\n */\n\nimport axios, { } from \"axios\";\n\n\n\n\n\n\n\n\nimport { HttpClientError } from \"../errors.js\";\nimport { classifyError, getErrorDescription } from \"./errors.js\";\nimport { calculateRetryDelay, sleep, shouldRetryError, } from \"./retry.js\";\n\nexport { HttpClientError };\n\n/**\n * Resilient HTTP Client with automatic retry logic.\n * Use HttpClient.init(context, options) when running with init/context; constructor(config) for standalone.\n */\nexport class HttpClient {\n axiosInstance;\n config;\n logger;\n\n constructor(contextOrConfig = {}, config) {\n const hasContext = config !== undefined;\n const options = hasContext ? config : (contextOrConfig || {});\n const context = hasContext ? contextOrConfig : undefined;\n\n this.config = {\n ...options,\n logger: context?.logger ?? options.logger ?? console,\n };\n this.logger = this.config.logger;\n\n this.axiosInstance = axios.create({\n timeout: this.config.timeout,\n validateStatus: () => true,\n maxRedirects: this.config.maxRedirects,\n headers: { \"User-Agent\": this.config.userAgent },\n httpsAgent: this.config.validateSSL ? undefined : { rejectUnauthorized: false } ,\n });\n\n // Add response interceptor for logging (optional - only if debug enabled)\n this.axiosInstance.interceptors.response.use(\n (response) => response,\n (error) => {\n // Log network-level errors here if needed\n // (HTTP errors are handled in the request method)\n return Promise.reject(error);\n }\n );\n }\n\n /**\n * Static init - discovers params via getAllForModule(defs). Whatever is in options goes.\n */\n static init(context, options = {}) {\n const defs = {\n timeout: \"number default 30000\",\n retryCount: \"number default 3\",\n retryDelay: \"number default 1000\",\n maxRetryDelay: \"number default 30000\",\n retryJitter: \"number default 0.1\",\n userAgent: \"string default HttpClient/v1.0\",\n validateSSL: \"boolean default true\",\n maxRedirects: \"number default 5\",\n };\n const discovered = context?.params?.getAllForModule?.(defs) ?? {};\n const merged = { ...discovered, ...options, logger: options.logger ?? context?.logger };\n return new HttpClient(context, merged);\n }\n\n /**\n * Make an HTTP request with automatic retry logic\n * Never throws - always returns HttpClientResponse\n */\n async request(\n method,\n url,\n options = {}\n ) {\n const startTime = Date.now();\n\n // Merge request options with defaults\n const requestConfig = {\n method,\n url,\n timeout: options.timeout ?? this.config.timeout,\n headers: {\n \"User-Agent\": options.userAgent ?? this.config.userAgent,\n ...options.headers\n },\n params: options.params,\n data: options.data\n };\n\n const retryCount = options.retryCount ?? this.config.retryCount;\n const retryDelay = options.retryDelay ?? this.config.retryDelay;\n\n // Initialize retry context\n const _retryContext = null;\n let _lastError = null;\n\n // Attempt the request with retries\n for (let attempt = 1; attempt <= retryCount + 1; attempt++) {\n try {\n if (options.debug) {\n this.logger.debug?.(`[HttpClient] ${method} ${url} (attempt ${attempt}/${retryCount + 1})`);\n }\n\n const response = await this.axiosInstance.request(requestConfig);\n const duration = Date.now() - startTime;\n\n // Success! Return unified response format\n const customStatus = this.mapHttpStatusToCustomStatus(response.status);\n\n if (options.debug) {\n this.logger.debug?.(`[HttpClient] ${method} ${url} → ${response.status} ${customStatus} (${duration}ms)`);\n }\n\n return {\n status: customStatus,\n code: response.status,\n headers: response.headers ,\n data: response.data,\n duration,\n retryCount: attempt - 1,\n finalUrl: response.request?.res?.responseUrl || url\n };\n\n } catch (error) {\n _lastError = error;\n const duration = Date.now() - startTime;\n\n // Classify the error\n const classification = classifyError(error);\n const errorDescription = getErrorDescription(classification.type);\n\n // Log the error\n if (classification.retryable && attempt <= retryCount) {\n this.logger.warn?.(`[HttpClient] ${method} ${url} failed (${classification.type}): ${errorDescription}. Retrying in ${retryDelay}ms...`);\n } else if (!classification.retryable || attempt > retryCount) {\n this.logger.error?.(`[HttpClient] ${method} ${url} failed (${classification.type}): ${errorDescription}`);\n }\n\n // Check if we should retry\n if (attempt <= retryCount && shouldRetryError(classification)) {\n // Calculate delay and wait\n const delay = calculateRetryDelay(\n attempt,\n retryDelay,\n this.config.maxRetryDelay,\n this.config.retryJitter\n );\n\n if (options.debug) {\n this.logger.debug?.(`[HttpClient] Waiting ${delay}ms before retry ${attempt + 1}`);\n }\n\n await sleep(delay);\n continue;\n }\n\n // No more retries or not retryable - return error response\n return {\n status: classification.status,\n code: error.response?.status || null,\n error: classification.type,\n headers: error.response?.headers || null,\n data: error.response?.data || null,\n duration,\n retryCount: attempt - 1,\n finalUrl: url\n };\n }\n }\n\n // This should never be reached, but just in case\n return {\n status: \"unknown\",\n code: null,\n error: \"unknown\",\n headers: null,\n data: null,\n duration: Date.now() - startTime,\n retryCount: retryCount,\n finalUrl: url\n };\n }\n\n /**\n * GET request\n */\n async get(url, options = {}) {\n return this.request(\"GET\", url, options);\n }\n\n /**\n * POST request\n */\n async post(url, options = {}) {\n return this.request(\"POST\", url, options);\n }\n\n /**\n * PUT request\n */\n async put(url, options = {}) {\n return this.request(\"PUT\", url, options);\n }\n\n /**\n * DELETE request\n */\n async delete(url, options = {}) {\n return this.request(\"DELETE\", url, options);\n }\n\n /**\n * PATCH request\n */\n async patch(url, options = {}) {\n return this.request(\"PATCH\", url, options);\n }\n\n /**\n * HEAD request\n */\n async head(url, options = {}) {\n return this.request(\"HEAD\", url, options);\n }\n\n /**\n * OPTIONS request\n */\n async options(url, options = {}) {\n return this.request(\"OPTIONS\", url, options);\n }\n\n /**\n * Map HTTP status code to custom status\n */\n mapHttpStatusToCustomStatus(httpStatus) {\n if (httpStatus >= 200 && httpStatus < 300) {\n return \"success\";\n } else if (httpStatus === 401) {\n return \"authRequired\";\n } else if (httpStatus === 403) {\n return \"authFailed\";\n } else if (httpStatus >= 400 && httpStatus < 500) {\n return \"clientError\";\n } else if (httpStatus >= 500) {\n return \"serverError\";\n }\n return \"unknown\";\n }\n\n /**\n * Get current configuration (for debugging)\n */\n getConfig() {\n return { ...this.config };\n }\n}\n","/**\n * Error classes for the CLI toolkit\n */\n\nexport class FrameworkError extends Error {\n constructor(message) {\n super(message);\n this.name = \"FrameworkError\";\n }\n}\n\nexport class ParamError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"ParamError\";\n }\n}\n\nexport class InitError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"InitError\";\n }\n}\n\nexport class CriticalRequestError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"CriticalRequestError\";\n }\n}\n\nexport class ControlFlowError extends Error {\n constructor(message) {\n super(message);\n this.name = \"ControlFlowError\";\n }\n}\n\nexport class HttpClientError extends FrameworkError {\n constructor(message, cause) {\n super(message);this.cause = cause;;\n this.name = \"HttpClientError\";\n }\n}\n\nexport class FileDatabaseError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"FileDatabaseError\";\n }\n}\n\n","\n\n\n\n\n\n\n\n/**\n * Classify an error and determine retry behavior\n */\nexport function classifyError(error) {\n // Handle axios response errors (HTTP errors)\n if (error.response) {\n const { status } = error.response;\n\n switch (status) {\n case 400:\n return { type: \"badRequest\", retryable: false, isAuth: false, status: \"clientError\" };\n case 401:\n return { type: \"unauthorized\", retryable: false, isAuth: true, status: \"authRequired\" };\n case 403:\n return { type: \"forbidden\", retryable: false, isAuth: true, status: \"authFailed\" };\n case 404:\n return { type: \"notFound\", retryable: false, isAuth: false, status: \"clientError\" };\n case 405:\n return { type: \"methodNotAllowed\", retryable: false, isAuth: false, status: \"clientError\" };\n case 409:\n return { type: \"conflict\", retryable: false, isAuth: false, status: \"clientError\" };\n case 422:\n return { type: \"unprocessableEntity\", retryable: false, isAuth: false, status: \"clientError\" };\n case 429:\n return { type: \"tooManyRequests\", retryable: true, isAuth: false, status: \"clientError\" };\n case 500:\n return { type: \"internalServerError\", retryable: true, isAuth: false, status: \"serverError\" };\n case 502:\n return { type: \"badGateway\", retryable: true, isAuth: false, status: \"serverError\" };\n case 503:\n return { type: \"serviceUnavailable\", retryable: true, isAuth: false, status: \"serverError\" };\n case 504:\n return { type: \"gatewayTimeout\", retryable: true, isAuth: false, status: \"serverError\" };\n default:\n if (status >= 400 && status < 500) {\n return { type: \"clientError\" , retryable: false, isAuth: false, status: \"clientError\" };\n } else if (status >= 500) {\n return { type: \"serverError\" , retryable: true, isAuth: false, status: \"serverError\" };\n }\n break;\n }\n }\n\n // Handle network/connection errors (no response)\n if (error.code) {\n switch (error.code) {\n case \"ECONNREFUSED\":\n case \"ECONNRESET\":\n case \"EPIPE\":\n case \"ENOTFOUND\":\n case \"EHOSTUNREACH\":\n case \"ENETUNREACH\":\n return { type: \"connectionFailed\", retryable: true, isAuth: false, status: \"networkError\" };\n\n case \"ETIMEDOUT\":\n case \"ECONNABORTED\":\n case \"ESOCKETTIMEDOUT\":\n return { type: \"timeout\", retryable: true, isAuth: false, status: \"timeout\" };\n\n case \"EAUTH\":\n case \"EACCES\":\n return { type: \"unauthorized\", retryable: false, isAuth: true, status: \"authRequired\" };\n\n default:\n return { type: \"networkError\", retryable: true, isAuth: false, status: \"networkError\" };\n }\n }\n\n // Handle timeout errors\n if (error.message && (\n error.message.includes(\"timeout\") ||\n error.message.includes(\"TIMEOUT\") ||\n error.message.includes(\"aborted\")\n )) {\n return { type: \"timeout\", retryable: true, isAuth: false, status: \"timeout\" };\n }\n\n // Handle cancellation\n if (error.name === \"AbortError\" || error.message?.includes(\"cancelled\")) {\n return { type: \"requestCancelled\", retryable: false, isAuth: false, status: \"unknown\" };\n }\n\n // Default fallback\n return { type: \"unknown\", retryable: false, isAuth: false, status: \"unknown\" };\n}\n\n/**\n * Get a human-readable description for an error type\n */\nexport function getErrorDescription(errorType) {\n const descriptions = {\n connectionFailed: \"Failed to establish a connection to the server\",\n timeout: \"Request timed out before completing\",\n networkError: \"Network connection issue occurred\",\n badRequest: \"Request was malformed or invalid\",\n unauthorized: \"Authentication credentials are required\",\n forbidden: \"Access to the requested resource is forbidden\",\n notFound: \"The requested resource was not found\",\n methodNotAllowed: \"HTTP method not allowed for this resource\",\n conflict: \"Request conflicts with current server state\",\n unprocessableEntity: \"Request data could not be processed\",\n tooManyRequests: \"Too many requests sent in a short time\",\n internalServerError: \"Server encountered an internal error\",\n badGateway: \"Invalid response from upstream server\",\n serviceUnavailable: \"Server is temporarily unavailable\",\n gatewayTimeout: \"Upstream server timed out\",\n unknown: \"An unknown error occurred\",\n requestCancelled: \"Request was cancelled before completion\"\n };\n\n return descriptions[errorType] || \"An unknown error occurred\";\n}\n","\n\n\n\n\n\n\n\n/**\n * Calculate the next retry delay using exponential backoff with jitter\n *\n * Formula: delay = baseDelay * (2 ^ (attempt - 1)) + randomJitter\n *\n * Jitter prevents the \"thundering herd\" problem where multiple failed requests\n * all retry at the exact same time, overwhelming the server.\n */\nexport function calculateRetryDelay(\n attempt,\n baseDelay,\n maxDelay,\n jitterFactor = 0.1\n) {\n // Exponential backoff: baseDelay * (2 ^ (attempt - 1))\n const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);\n\n // Cap at maximum delay\n const cappedDelay = Math.min(exponentialDelay, maxDelay);\n\n // Add jitter: random variation up to jitterFactor of the delay\n const jitter = cappedDelay * jitterFactor * Math.random();\n\n return Math.floor(cappedDelay + jitter);\n}\n\n/**\n * Sleep for the specified number of milliseconds\n */\nexport function sleep(ms) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Determine if an error should be retried based on classification\n */\nexport function shouldRetryError(classification) {\n // Never retry auth errors (they won't succeed with the same credentials)\n if (classification.isAuth) {\n return false;\n }\n\n // Retry if the error is classified as retryable\n return classification.retryable;\n}\n\n/**\n * Create a retry context for tracking retry attempts\n */\nexport function createRetryContext(\n maxAttempts,\n baseDelay,\n maxDelay,\n jitterFactor\n) {\n return {\n attempt: 1,\n maxAttempts,\n lastError: new Error(\"Initial attempt\"),\n totalDelay: 0,\n nextDelay: calculateRetryDelay(1, baseDelay, maxDelay, jitterFactor)\n };\n}\n\n/**\n * Update retry context for the next attempt\n */\nexport function updateRetryContext(\n context,\n lastError,\n baseDelay,\n maxDelay,\n jitterFactor\n) {\n const nextAttempt = context.attempt + 1;\n const nextDelay = calculateRetryDelay(nextAttempt, baseDelay, maxDelay, jitterFactor);\n\n return {\n attempt: nextAttempt,\n maxAttempts: context.maxAttempts,\n lastError,\n totalDelay: context.totalDelay + context.nextDelay,\n nextDelay\n };\n}\n"],"mappings":";AAYA,OAAO,eAAgB;;;ACRhB,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACtC,YAAY,SAAS;AACjB,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EAChB;AACJ;AA8BO,IAAM,kBAAN,cAA8B,eAAe;AAAA,EAChD,YAAY,SAAW,OAAO;AAC1B,UAAM,OAAO;AAAE,SAAK,QAAQ;AAAM;AAClC,SAAK,OAAO;AAAA,EAChB;AACJ;;;ACjCO,SAAS,cAAc,OAAO;AAEjC,MAAI,MAAM,UAAU;AAChB,UAAM,EAAE,OAAO,IAAI,MAAM;AAEzB,YAAQ,QAAQ;AAAA,MACZ,KAAK;AACD,eAAO,EAAE,MAAM,cAAc,WAAW,OAAO,QAAQ,OAAO,QAAQ,cAAc;AAAA,MACxF,KAAK;AACD,eAAO,EAAE,MAAM,gBAAgB,WAAW,OAAO,QAAQ,MAAM,QAAQ,eAAe;AAAA,MAC1F,KAAK;AACD,eAAO,EAAE,MAAM,aAAa,WAAW,OAAO,QAAQ,MAAM,QAAQ,aAAa;AAAA,MACrF,KAAK;AACD,eAAO,EAAE,MAAM,YAAY,WAAW,OAAO,QAAQ,OAAO,QAAQ,cAAc;AAAA,MACtF,KAAK;AACD,eAAO,EAAE,MAAM,oBAAoB,WAAW,OAAO,QAAQ,OAAO,QAAQ,cAAc;AAAA,MAC9F,KAAK;AACD,eAAO,EAAE,MAAM,YAAY,WAAW,OAAO,QAAQ,OAAO,QAAQ,cAAc;AAAA,MACtF,KAAK;AACD,eAAO,EAAE,MAAM,uBAAuB,WAAW,OAAO,QAAQ,OAAO,QAAQ,cAAc;AAAA,MACjG,KAAK;AACD,eAAO,EAAE,MAAM,mBAAmB,WAAW,MAAM,QAAQ,OAAO,QAAQ,cAAc;AAAA,MAC5F,KAAK;AACD,eAAO,EAAE,MAAM,uBAAuB,WAAW,MAAM,QAAQ,OAAO,QAAQ,cAAc;AAAA,MAChG,KAAK;AACD,eAAO,EAAE,MAAM,cAAc,WAAW,MAAM,QAAQ,OAAO,QAAQ,cAAc;AAAA,MACvF,KAAK;AACD,eAAO,EAAE,MAAM,sBAAsB,WAAW,MAAM,QAAQ,OAAO,QAAQ,cAAc;AAAA,MAC/F,KAAK;AACD,eAAO,EAAE,MAAM,kBAAkB,WAAW,MAAM,QAAQ,OAAO,QAAQ,cAAc;AAAA,MAC3F;AACI,YAAI,UAAU,OAAO,SAAS,KAAK;AAC/B,iBAAO,EAAE,MAAM,eAAgB,WAAW,OAAO,QAAQ,OAAO,QAAQ,cAAc;AAAA,QAC1F,WAAW,UAAU,KAAK;AACtB,iBAAO,EAAE,MAAM,eAAgB,WAAW,MAAM,QAAQ,OAAO,QAAQ,cAAc;AAAA,QACzF;AACA;AAAA,IACR;AAAA,EACJ;AAGA,MAAI,MAAM,MAAM;AACZ,YAAQ,MAAM,MAAM;AAAA,MAChB,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AACD,eAAO,EAAE,MAAM,oBAAoB,WAAW,MAAM,QAAQ,OAAO,QAAQ,eAAe;AAAA,MAE9F,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AACD,eAAO,EAAE,MAAM,WAAW,WAAW,MAAM,QAAQ,OAAO,QAAQ,UAAU;AAAA,MAEhF,KAAK;AAAA,MACL,KAAK;AACD,eAAO,EAAE,MAAM,gBAAgB,WAAW,OAAO,QAAQ,MAAM,QAAQ,eAAe;AAAA,MAE1F;AACI,eAAO,EAAE,MAAM,gBAAgB,WAAW,MAAM,QAAQ,OAAO,QAAQ,eAAe;AAAA,IAC9F;AAAA,EACJ;AAGA,MAAI,MAAM,YACN,MAAM,QAAQ,SAAS,SAAS,KAChC,MAAM,QAAQ,SAAS,SAAS,KAChC,MAAM,QAAQ,SAAS,SAAS,IACjC;AACC,WAAO,EAAE,MAAM,WAAW,WAAW,MAAM,QAAQ,OAAO,QAAQ,UAAU;AAAA,EAChF;AAGA,MAAI,MAAM,SAAS,gBAAgB,MAAM,SAAS,SAAS,WAAW,GAAG;AACrE,WAAO,EAAE,MAAM,oBAAoB,WAAW,OAAO,QAAQ,OAAO,QAAQ,UAAU;AAAA,EAC1F;AAGA,SAAO,EAAE,MAAM,WAAW,WAAW,OAAO,QAAQ,OAAO,QAAQ,UAAU;AACjF;AAKO,SAAS,oBAAoB,WAAW;AAC3C,QAAM,eAAe;AAAA,IACjB,kBAAkB;AAAA,IAClB,SAAS;AAAA,IACT,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,WAAW;AAAA,IACX,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,qBAAqB;AAAA,IACrB,iBAAiB;AAAA,IACjB,qBAAqB;AAAA,IACrB,YAAY;AAAA,IACZ,oBAAoB;AAAA,IACpB,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,kBAAkB;AAAA,EACtB;AAEA,SAAO,aAAa,SAAS,KAAK;AACtC;;;ACvGO,SAAS,oBACZ,SACA,WACA,UACA,eAAe,KACjB;AAEE,QAAM,mBAAmB,YAAY,KAAK,IAAI,GAAG,UAAU,CAAC;AAG5D,QAAM,cAAc,KAAK,IAAI,kBAAkB,QAAQ;AAGvD,QAAM,SAAS,cAAc,eAAe,KAAK,OAAO;AAExD,SAAO,KAAK,MAAM,cAAc,MAAM;AAC1C;AAKO,SAAS,MAAM,IAAI;AACtB,SAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AACzD;AAKO,SAAS,iBAAiB,gBAAgB;AAE7C,MAAI,eAAe,QAAQ;AACvB,WAAO;AAAA,EACX;AAGA,SAAO,eAAe;AAC1B;;;AHrBO,IAAM,aAAN,MAAM,YAAW;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YAAY,kBAAkB,CAAC,GAAG,QAAQ;AACtC,UAAM,aAAa,WAAW;AAC9B,UAAM,UAAU,aAAa,SAAU,mBAAmB,CAAC;AAC3D,UAAM,UAAU,aAAa,kBAAkB;AAE/C,SAAK,SAAS;AAAA,MACV,GAAG;AAAA,MACH,QAAQ,SAAS,UAAU,QAAQ,UAAU;AAAA,IACjD;AACA,SAAK,SAAS,KAAK,OAAO;AAE1B,SAAK,gBAAgB,MAAM,OAAO;AAAA,MAC9B,SAAS,KAAK,OAAO;AAAA,MACrB,gBAAgB,MAAM;AAAA,MACtB,cAAc,KAAK,OAAO;AAAA,MAC1B,SAAS,EAAE,cAAc,KAAK,OAAO,UAAU;AAAA,MAC/C,YAAY,KAAK,OAAO,cAAc,SAAY,EAAE,oBAAoB,MAAM;AAAA,IAClF,CAAC;AAGD,SAAK,cAAc,aAAa,SAAS;AAAA,MACrC,CAAC,aAAa;AAAA,MACd,CAAC,UAAU;AAGP,eAAO,QAAQ,OAAO,KAAK;AAAA,MAC/B;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,KAAK,SAAS,UAAU,CAAC,GAAG;AAC/B,UAAM,OAAO;AAAA,MACT,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,WAAW;AAAA,MACX,aAAa;AAAA,MACb,cAAc;AAAA,IAClB;AACA,UAAM,aAAa,SAAS,QAAQ,kBAAkB,IAAI,KAAK,CAAC;AAChE,UAAM,SAAS,EAAE,GAAG,YAAY,GAAG,SAAS,QAAQ,QAAQ,UAAU,SAAS,OAAO;AACtF,WAAO,IAAI,YAAW,SAAS,MAAM;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QACF,QACA,KACA,UAAU,CAAC,GACb;AACE,UAAM,YAAY,KAAK,IAAI;AAG3B,UAAM,gBAAgB;AAAA,MAClB;AAAA,MACA;AAAA,MACA,SAAS,QAAQ,WAAW,KAAK,OAAO;AAAA,MACxC,SAAS;AAAA,QACL,cAAc,QAAQ,aAAa,KAAK,OAAO;AAAA,QAC/C,GAAG,QAAQ;AAAA,MACf;AAAA,MACA,QAAQ,QAAQ;AAAA,MAChB,MAAM,QAAQ;AAAA,IAClB;AAEA,UAAM,aAAa,QAAQ,cAAc,KAAK,OAAO;AACrD,UAAM,aAAa,QAAQ,cAAc,KAAK,OAAO;AAGrD,UAAM,gBAAgB;AACtB,QAAI,aAAa;AAGjB,aAAS,UAAU,GAAG,WAAW,aAAa,GAAG,WAAW;AACxD,UAAI;AACA,YAAI,QAAQ,OAAO;AACf,eAAK,OAAO,QAAQ,gBAAgB,MAAM,IAAI,GAAG,aAAa,OAAO,IAAI,aAAa,CAAC,GAAG;AAAA,QAC9F;AAEA,cAAM,WAAW,MAAM,KAAK,cAAc,QAAQ,aAAa;AAC/D,cAAM,WAAW,KAAK,IAAI,IAAI;AAG9B,cAAM,eAAe,KAAK,4BAA4B,SAAS,MAAM;AAErE,YAAI,QAAQ,OAAO;AACf,eAAK,OAAO,QAAQ,gBAAgB,MAAM,IAAI,GAAG,WAAM,SAAS,MAAM,IAAI,YAAY,KAAK,QAAQ,KAAK;AAAA,QAC5G;AAEA,eAAO;AAAA,UACH,QAAQ;AAAA,UACR,MAAM,SAAS;AAAA,UACf,SAAS,SAAS;AAAA,UAClB,MAAM,SAAS;AAAA,UACf;AAAA,UACA,YAAY,UAAU;AAAA,UACtB,UAAU,SAAS,SAAS,KAAK,eAAe;AAAA,QACpD;AAAA,MAEJ,SAAS,OAAO;AACZ,qBAAa;AACb,cAAM,WAAW,KAAK,IAAI,IAAI;AAG9B,cAAM,iBAAiB,cAAc,KAAK;AAC1C,cAAM,mBAAmB,oBAAoB,eAAe,IAAI;AAGhE,YAAI,eAAe,aAAa,WAAW,YAAY;AACnD,eAAK,OAAO,OAAO,gBAAgB,MAAM,IAAI,GAAG,YAAY,eAAe,IAAI,MAAM,gBAAgB,iBAAiB,UAAU,OAAO;AAAA,QAC3I,WAAW,CAAC,eAAe,aAAa,UAAU,YAAY;AAC1D,eAAK,OAAO,QAAQ,gBAAgB,MAAM,IAAI,GAAG,YAAY,eAAe,IAAI,MAAM,gBAAgB,EAAE;AAAA,QAC5G;AAGA,YAAI,WAAW,cAAc,iBAAiB,cAAc,GAAG;AAE3D,gBAAM,QAAQ;AAAA,YACV;AAAA,YACA;AAAA,YACA,KAAK,OAAO;AAAA,YACZ,KAAK,OAAO;AAAA,UAChB;AAEA,cAAI,QAAQ,OAAO;AACf,iBAAK,OAAO,QAAQ,wBAAwB,KAAK,mBAAmB,UAAU,CAAC,EAAE;AAAA,UACrF;AAEA,gBAAM,MAAM,KAAK;AACjB;AAAA,QACJ;AAGA,eAAO;AAAA,UACH,QAAQ,eAAe;AAAA,UACvB,MAAM,MAAM,UAAU,UAAU;AAAA,UAChC,OAAO,eAAe;AAAA,UACtB,SAAS,MAAM,UAAU,WAAW;AAAA,UACpC,MAAM,MAAM,UAAU,QAAQ;AAAA,UAC9B;AAAA,UACA,YAAY,UAAU;AAAA,UACtB,UAAU;AAAA,QACd;AAAA,MACJ;AAAA,IACJ;AAGA,WAAO;AAAA,MACH,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO;AAAA,MACP,SAAS;AAAA,MACT,MAAM;AAAA,MACN,UAAU,KAAK,IAAI,IAAI;AAAA,MACvB;AAAA,MACA,UAAU;AAAA,IACd;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAI,KAAK,UAAU,CAAC,GAAG;AACzB,WAAO,KAAK,QAAQ,OAAO,KAAK,OAAO;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KAAK,KAAK,UAAU,CAAC,GAAG;AAC1B,WAAO,KAAK,QAAQ,QAAQ,KAAK,OAAO;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAI,KAAK,UAAU,CAAC,GAAG;AACzB,WAAO,KAAK,QAAQ,OAAO,KAAK,OAAO;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO,KAAK,UAAU,CAAC,GAAG;AAC5B,WAAO,KAAK,QAAQ,UAAU,KAAK,OAAO;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM,KAAK,UAAU,CAAC,GAAG;AAC3B,WAAO,KAAK,QAAQ,SAAS,KAAK,OAAO;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KAAK,KAAK,UAAU,CAAC,GAAG;AAC1B,WAAO,KAAK,QAAQ,QAAQ,KAAK,OAAO;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,KAAK,UAAU,CAAC,GAAG;AAC7B,WAAO,KAAK,QAAQ,WAAW,KAAK,OAAO;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKA,4BAA4B,YAAY;AACpC,QAAI,cAAc,OAAO,aAAa,KAAK;AACvC,aAAO;AAAA,IACX,WAAW,eAAe,KAAK;AAC3B,aAAO;AAAA,IACX,WAAW,eAAe,KAAK;AAC3B,aAAO;AAAA,IACX,WAAW,cAAc,OAAO,aAAa,KAAK;AAC9C,aAAO;AAAA,IACX,WAAW,cAAc,KAAK;AAC1B,aAAO;AAAA,IACX;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY;AACR,WAAO,EAAE,GAAG,KAAK,OAAO;AAAA,EAC5B;AACJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/http-client/index.js","../src/errors.js","../src/http-client/errors.js","../src/http-client/retry.js"],"sourcesContent":["/**\n * HttpClient - Resilient HTTP Client with Retry Logic\n *\n * A production-ready HTTP client that:\n * - Wraps axios with enhanced error handling and retry logic\n * - Never throws exceptions - always returns unified response format\n * - Uses exponential backoff with jitter for retries\n * - Provides human-readable error classifications\n * - Supports comprehensive logging\n * - Handles all HTTP methods consistently\n */\n\nimport axios, { } from \"axios\";\n\n\n\n\n\n\n\n\nimport { HttpClientError } from \"../errors.js\";\nimport { classifyError, getErrorDescription } from \"./errors.js\";\nimport { calculateRetryDelay, sleep, shouldRetryError, } from \"./retry.js\";\n\nexport { HttpClientError };\n\n/**\n * Resilient HTTP Client with automatic retry logic.\n * Use HttpClient.init(context, options) when running with init/context; constructor(config) for standalone.\n */\nexport class HttpClient {\n axiosInstance;\n config;\n logger;\n\n constructor(contextOrConfig = {}, config) {\n const hasContext = config !== undefined;\n const options = hasContext ? config : (contextOrConfig || {});\n const context = hasContext ? contextOrConfig : undefined;\n\n this.config = {\n ...options,\n logger: context?.logger ?? options.logger ?? console,\n };\n this.logger = this.config.logger;\n\n this.axiosInstance = axios.create({\n timeout: this.config.timeout,\n validateStatus: () => true,\n maxRedirects: this.config.maxRedirects,\n headers: { \"User-Agent\": this.config.userAgent },\n httpsAgent: this.config.validateSSL ? undefined : { rejectUnauthorized: false } ,\n });\n\n // Add response interceptor for logging (optional - only if debug enabled)\n this.axiosInstance.interceptors.response.use(\n (response) => response,\n (error) => {\n // Log network-level errors here if needed\n // (HTTP errors are handled in the request method)\n return Promise.reject(error);\n }\n );\n }\n\n /**\n * Static init - discovers params via getAllForModule(defs). Whatever is in options goes.\n */\n static init(context, options = {}) {\n const defs = {\n timeout: \"number default 30000\",\n retryCount: \"number default 3\",\n retryDelay: \"number default 1000\",\n maxRetryDelay: \"number default 30000\",\n retryJitter: \"number default 0.1\",\n userAgent: \"string default HttpClient/v1.0\",\n validateSSL: \"boolean default true\",\n maxRedirects: \"number default 5\",\n };\n const discovered = context?.params?.getAllForModule?.(defs) ?? {};\n const merged = { ...discovered, ...options, logger: options.logger ?? context?.logger };\n return new HttpClient(context, merged);\n }\n\n /**\n * Make an HTTP request with automatic retry logic\n * Never throws - always returns HttpClientResponse\n */\n async request(\n method,\n url,\n options = {}\n ) {\n const startTime = Date.now();\n\n // Merge request options with defaults\n const requestConfig = {\n method,\n url,\n timeout: options.timeout ?? this.config.timeout,\n headers: {\n \"User-Agent\": options.userAgent ?? this.config.userAgent,\n ...options.headers\n },\n params: options.params,\n data: options.data\n };\n\n const retryCount = options.retryCount ?? this.config.retryCount;\n const retryDelay = options.retryDelay ?? this.config.retryDelay;\n\n // Initialize retry context\n const _retryContext = null;\n let _lastError = null;\n\n // Attempt the request with retries\n for (let attempt = 1; attempt <= retryCount + 1; attempt++) {\n try {\n if (options.debug) {\n this.logger.debug?.(`[HttpClient] ${method} ${url} (attempt ${attempt}/${retryCount + 1})`);\n }\n\n const response = await this.axiosInstance.request(requestConfig);\n const duration = Date.now() - startTime;\n\n // Success! Return unified response format\n const customStatus = this.mapHttpStatusToCustomStatus(response.status);\n\n if (options.debug) {\n this.logger.debug?.(`[HttpClient] ${method} ${url} → ${response.status} ${customStatus} (${duration}ms)`);\n }\n\n return {\n status: customStatus,\n code: response.status,\n headers: response.headers ,\n data: response.data,\n duration,\n retryCount: attempt - 1,\n finalUrl: response.request?.res?.responseUrl || url\n };\n\n } catch (error) {\n _lastError = error;\n const duration = Date.now() - startTime;\n\n // Classify the error\n const classification = classifyError(error);\n const errorDescription = getErrorDescription(classification.type);\n\n // Log the error\n if (classification.retryable && attempt <= retryCount) {\n this.logger.warn?.(`[HttpClient] ${method} ${url} failed (${classification.type}): ${errorDescription}. Retrying in ${retryDelay}ms...`);\n } else if (!classification.retryable || attempt > retryCount) {\n this.logger.error?.(`[HttpClient] ${method} ${url} failed (${classification.type}): ${errorDescription}`);\n }\n\n // Check if we should retry\n if (attempt <= retryCount && shouldRetryError(classification)) {\n // Calculate delay and wait\n const delay = calculateRetryDelay(\n attempt,\n retryDelay,\n this.config.maxRetryDelay,\n this.config.retryJitter\n );\n\n if (options.debug) {\n this.logger.debug?.(`[HttpClient] Waiting ${delay}ms before retry ${attempt + 1}`);\n }\n\n await sleep(delay);\n continue;\n }\n\n // No more retries or not retryable - return error response\n return {\n status: classification.status,\n code: error.response?.status || null,\n error: classification.type,\n headers: error.response?.headers || null,\n data: error.response?.data || null,\n duration,\n retryCount: attempt - 1,\n finalUrl: url\n };\n }\n }\n\n // This should never be reached, but just in case\n return {\n status: \"unknown\",\n code: null,\n error: \"unknown\",\n headers: null,\n data: null,\n duration: Date.now() - startTime,\n retryCount: retryCount,\n finalUrl: url\n };\n }\n\n /**\n * GET request\n */\n async get(url, options = {}) {\n return this.request(\"GET\", url, options);\n }\n\n /**\n * POST request\n */\n async post(url, options = {}) {\n return this.request(\"POST\", url, options);\n }\n\n /**\n * PUT request\n */\n async put(url, options = {}) {\n return this.request(\"PUT\", url, options);\n }\n\n /**\n * DELETE request\n */\n async delete(url, options = {}) {\n return this.request(\"DELETE\", url, options);\n }\n\n /**\n * PATCH request\n */\n async patch(url, options = {}) {\n return this.request(\"PATCH\", url, options);\n }\n\n /**\n * HEAD request\n */\n async head(url, options = {}) {\n return this.request(\"HEAD\", url, options);\n }\n\n /**\n * OPTIONS request\n */\n async options(url, options = {}) {\n return this.request(\"OPTIONS\", url, options);\n }\n\n /**\n * Map HTTP status code to custom status\n */\n mapHttpStatusToCustomStatus(httpStatus) {\n if (httpStatus >= 200 && httpStatus < 300) {\n return \"success\";\n } else if (httpStatus === 401) {\n return \"authRequired\";\n } else if (httpStatus === 403) {\n return \"authFailed\";\n } else if (httpStatus >= 400 && httpStatus < 500) {\n return \"clientError\";\n } else if (httpStatus >= 500) {\n return \"serverError\";\n }\n return \"unknown\";\n }\n\n /**\n * Get current configuration (for debugging)\n */\n getConfig() {\n return { ...this.config };\n }\n}\n","/**\n * Error classes for the CLI toolkit\n */\n\nexport class FrameworkError extends Error {\n constructor(message) {\n super(message);\n this.name = \"FrameworkError\";\n }\n}\n\nexport class ParamError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"ParamError\";\n }\n}\n\nexport class InitError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"InitError\";\n }\n}\n\nexport class CriticalRequestError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"CriticalRequestError\";\n }\n}\n\nexport class ControlFlowError extends Error {\n constructor(message) {\n super(message);\n this.name = \"ControlFlowError\";\n }\n}\n\nexport class HttpClientError extends FrameworkError {\n constructor(message, cause) {\n super(message);this.cause = cause;;\n this.name = \"HttpClientError\";\n }\n}\n\nexport class FileDatabaseError extends FrameworkError {\n constructor(message) {\n super(message);\n this.name = \"FileDatabaseError\";\n }\n}\n\n","\n\n\n\n\n\n\n\n/**\n * Classify an error and determine retry behavior\n */\nexport function classifyError(error) {\n // Handle axios response errors (HTTP errors)\n if (error.response) {\n const { status } = error.response;\n\n switch (status) {\n case 400:\n return { type: \"badRequest\", retryable: false, isAuth: false, status: \"clientError\" };\n case 401:\n return { type: \"unauthorized\", retryable: false, isAuth: true, status: \"authRequired\" };\n case 403:\n return { type: \"forbidden\", retryable: false, isAuth: true, status: \"authFailed\" };\n case 404:\n return { type: \"notFound\", retryable: false, isAuth: false, status: \"clientError\" };\n case 405:\n return { type: \"methodNotAllowed\", retryable: false, isAuth: false, status: \"clientError\" };\n case 409:\n return { type: \"conflict\", retryable: false, isAuth: false, status: \"clientError\" };\n case 422:\n return { type: \"unprocessableEntity\", retryable: false, isAuth: false, status: \"clientError\" };\n case 429:\n return { type: \"tooManyRequests\", retryable: true, isAuth: false, status: \"clientError\" };\n case 500:\n return { type: \"internalServerError\", retryable: true, isAuth: false, status: \"serverError\" };\n case 502:\n return { type: \"badGateway\", retryable: true, isAuth: false, status: \"serverError\" };\n case 503:\n return { type: \"serviceUnavailable\", retryable: true, isAuth: false, status: \"serverError\" };\n case 504:\n return { type: \"gatewayTimeout\", retryable: true, isAuth: false, status: \"serverError\" };\n default:\n if (status >= 400 && status < 500) {\n return { type: \"clientError\" , retryable: false, isAuth: false, status: \"clientError\" };\n } else if (status >= 500) {\n return { type: \"serverError\" , retryable: true, isAuth: false, status: \"serverError\" };\n }\n break;\n }\n }\n\n // Handle network/connection errors (no response)\n if (error.code) {\n switch (error.code) {\n case \"ECONNREFUSED\":\n case \"ECONNRESET\":\n case \"EPIPE\":\n case \"ENOTFOUND\":\n case \"EHOSTUNREACH\":\n case \"ENETUNREACH\":\n return { type: \"connectionFailed\", retryable: true, isAuth: false, status: \"networkError\" };\n\n case \"ETIMEDOUT\":\n case \"ECONNABORTED\":\n case \"ESOCKETTIMEDOUT\":\n return { type: \"timeout\", retryable: true, isAuth: false, status: \"timeout\" };\n\n case \"EAUTH\":\n case \"EACCES\":\n return { type: \"unauthorized\", retryable: false, isAuth: true, status: \"authRequired\" };\n\n default:\n return { type: \"networkError\", retryable: true, isAuth: false, status: \"networkError\" };\n }\n }\n\n // Handle timeout errors\n if (error.message && (\n error.message.includes(\"timeout\") ||\n error.message.includes(\"TIMEOUT\") ||\n error.message.includes(\"aborted\")\n )) {\n return { type: \"timeout\", retryable: true, isAuth: false, status: \"timeout\" };\n }\n\n // Handle cancellation\n if (error.name === \"AbortError\" || error.message?.includes(\"cancelled\")) {\n return { type: \"requestCancelled\", retryable: false, isAuth: false, status: \"unknown\" };\n }\n\n // Default fallback — treat as transient (socket/TLS glitches often lack a code).\n return { type: \"unknown\", retryable: true, isAuth: false, status: \"unknown\" };\n}\n\n/**\n * Get a human-readable description for an error type\n */\nexport function getErrorDescription(errorType) {\n const descriptions = {\n connectionFailed: \"Failed to establish a connection to the server\",\n timeout: \"Request timed out before completing\",\n networkError: \"Network connection issue occurred\",\n badRequest: \"Request was malformed or invalid\",\n unauthorized: \"Authentication credentials are required\",\n forbidden: \"Access to the requested resource is forbidden\",\n notFound: \"The requested resource was not found\",\n methodNotAllowed: \"HTTP method not allowed for this resource\",\n conflict: \"Request conflicts with current server state\",\n unprocessableEntity: \"Request data could not be processed\",\n tooManyRequests: \"Too many requests sent in a short time\",\n internalServerError: \"Server encountered an internal error\",\n badGateway: \"Invalid response from upstream server\",\n serviceUnavailable: \"Server is temporarily unavailable\",\n gatewayTimeout: \"Upstream server timed out\",\n unknown: \"An unknown error occurred\",\n requestCancelled: \"Request was cancelled before completion\"\n };\n\n return descriptions[errorType] || \"An unknown error occurred\";\n}\n","\n\n\n\n\n\n\n\n/**\n * Calculate the next retry delay using exponential backoff with jitter\n *\n * Formula: delay = baseDelay * (2 ^ (attempt - 1)) + randomJitter\n *\n * Jitter prevents the \"thundering herd\" problem where multiple failed requests\n * all retry at the exact same time, overwhelming the server.\n */\nexport function calculateRetryDelay(\n attempt,\n baseDelay,\n maxDelay,\n jitterFactor = 0.1\n) {\n // Exponential backoff: baseDelay * (2 ^ (attempt - 1))\n const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);\n\n // Cap at maximum delay\n const cappedDelay = Math.min(exponentialDelay, maxDelay);\n\n // Add jitter: random variation up to jitterFactor of the delay\n const jitter = cappedDelay * jitterFactor * Math.random();\n\n return Math.floor(cappedDelay + jitter);\n}\n\n/**\n * Sleep for the specified number of milliseconds\n */\nexport function sleep(ms) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Determine if an error should be retried based on classification\n */\nexport function shouldRetryError(classification) {\n // Never retry auth errors (they won't succeed with the same credentials)\n if (classification.isAuth) {\n return false;\n }\n\n // Retry if the error is classified as retryable\n return classification.retryable;\n}\n\n/**\n * Create a retry context for tracking retry attempts\n */\nexport function createRetryContext(\n maxAttempts,\n baseDelay,\n maxDelay,\n jitterFactor\n) {\n return {\n attempt: 1,\n maxAttempts,\n lastError: new Error(\"Initial attempt\"),\n totalDelay: 0,\n nextDelay: calculateRetryDelay(1, baseDelay, maxDelay, jitterFactor)\n };\n}\n\n/**\n * Update retry context for the next attempt\n */\nexport function updateRetryContext(\n context,\n lastError,\n baseDelay,\n maxDelay,\n jitterFactor\n) {\n const nextAttempt = context.attempt + 1;\n const nextDelay = calculateRetryDelay(nextAttempt, baseDelay, maxDelay, jitterFactor);\n\n return {\n attempt: nextAttempt,\n maxAttempts: context.maxAttempts,\n lastError,\n totalDelay: context.totalDelay + context.nextDelay,\n nextDelay\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -2456,23 +2456,18 @@ function defaultFileSynopsisFunction(fileEntry, data) {
|
|
|
2456
2456
|
const timestamps = [];
|
|
2457
2457
|
const statusCounts = {};
|
|
2458
2458
|
for (const item of data) {
|
|
2459
|
-
|
|
2460
|
-
let status = null;
|
|
2459
|
+
if (!item || typeof item !== "object") continue;
|
|
2461
2460
|
for (const [key, value] of Object.entries(item)) {
|
|
2462
2461
|
const k = key.toLowerCase();
|
|
2463
|
-
if (k
|
|
2464
|
-
ts = new Date(value).getTime();
|
|
2462
|
+
if (k.endsWith("modificationtimestamp") && value != null && value !== "") {
|
|
2463
|
+
const ts = new Date(value).getTime();
|
|
2464
|
+
if (!Number.isNaN(ts)) timestamps.push(ts);
|
|
2465
2465
|
}
|
|
2466
|
-
if (k === "standardstatus") {
|
|
2467
|
-
status = value;
|
|
2466
|
+
if (k === "standardstatus" && value != null && value !== "") {
|
|
2467
|
+
const status = String(value);
|
|
2468
|
+
statusCounts[status] = (statusCounts[status] || 0) + 1;
|
|
2468
2469
|
}
|
|
2469
2470
|
}
|
|
2470
|
-
if (ts && !isNaN(ts)) {
|
|
2471
|
-
timestamps.push(ts);
|
|
2472
|
-
}
|
|
2473
|
-
if (status !== null && status !== void 0) {
|
|
2474
|
-
statusCounts[status] = (statusCounts[status] || 0) + 1;
|
|
2475
|
-
}
|
|
2476
2471
|
}
|
|
2477
2472
|
const result = { ...fileEntry };
|
|
2478
2473
|
if (timestamps.length) {
|
|
@@ -2491,27 +2486,38 @@ function defaultVersionSynopsisFunction(metadata) {
|
|
|
2491
2486
|
const timestamps = [];
|
|
2492
2487
|
const statusCounts = {};
|
|
2493
2488
|
for (const file of metadata.files) {
|
|
2494
|
-
if (file
|
|
2489
|
+
if (file?.minModificationTimestamp) {
|
|
2495
2490
|
const minTs = new Date(file.minModificationTimestamp).getTime();
|
|
2496
|
-
if (!isNaN(minTs)) timestamps.push(minTs);
|
|
2491
|
+
if (!Number.isNaN(minTs)) timestamps.push(minTs);
|
|
2497
2492
|
}
|
|
2498
|
-
if (file
|
|
2493
|
+
if (file?.maxModificationTimestamp) {
|
|
2499
2494
|
const maxTs = new Date(file.maxModificationTimestamp).getTime();
|
|
2500
|
-
if (!isNaN(maxTs)) timestamps.push(maxTs);
|
|
2495
|
+
if (!Number.isNaN(maxTs)) timestamps.push(maxTs);
|
|
2501
2496
|
}
|
|
2502
|
-
if (file
|
|
2497
|
+
if (file?.StandardStatuses && typeof file.StandardStatuses === "object") {
|
|
2503
2498
|
for (const [status, count] of Object.entries(file.StandardStatuses)) {
|
|
2504
|
-
statusCounts[status] = (statusCounts[status] || 0) + count;
|
|
2499
|
+
statusCounts[status] = (statusCounts[status] || 0) + Number(count || 0);
|
|
2505
2500
|
}
|
|
2506
2501
|
}
|
|
2507
2502
|
}
|
|
2508
2503
|
const result = { ...metadata };
|
|
2504
|
+
const synopsis = {
|
|
2505
|
+
...metadata.synopsis && typeof metadata.synopsis === "object" ? metadata.synopsis : {}
|
|
2506
|
+
};
|
|
2509
2507
|
if (timestamps.length) {
|
|
2510
|
-
|
|
2511
|
-
|
|
2508
|
+
const minIso = new Date(Math.min(...timestamps)).toISOString();
|
|
2509
|
+
const maxIso = new Date(Math.max(...timestamps)).toISOString();
|
|
2510
|
+
result.minModificationTimestamp = minIso;
|
|
2511
|
+
result.maxModificationTimestamp = maxIso;
|
|
2512
|
+
synopsis.minModificationTimestamp = minIso;
|
|
2513
|
+
synopsis.maxModificationTimestamp = maxIso;
|
|
2512
2514
|
}
|
|
2513
2515
|
if (Object.keys(statusCounts).length > 0) {
|
|
2514
2516
|
result.StandardStatuses = statusCounts;
|
|
2517
|
+
synopsis.StandardStatuses = statusCounts;
|
|
2518
|
+
}
|
|
2519
|
+
if (Object.keys(synopsis).length) {
|
|
2520
|
+
result.synopsis = synopsis;
|
|
2515
2521
|
}
|
|
2516
2522
|
return result;
|
|
2517
2523
|
}
|
|
@@ -3645,10 +3651,35 @@ async function ensureSchemaEverywhere(dbs, spec, options = {}) {
|
|
|
3645
3651
|
// src/db/index.js
|
|
3646
3652
|
var KNEX_DEFAULTS = {
|
|
3647
3653
|
testConnection: true,
|
|
3648
|
-
|
|
3654
|
+
// min: 0 avoids holding idle sockets that go stale during long-running CLIs
|
|
3655
|
+
pool: { min: 0, max: 10, idleTimeoutMillis: 3e4 },
|
|
3649
3656
|
acquireConnectionTimeout: 1e4,
|
|
3650
3657
|
ssl: { rejectUnauthorized: false }
|
|
3651
3658
|
};
|
|
3659
|
+
var CONNECTION_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
3660
|
+
"ECONNRESET",
|
|
3661
|
+
"ECONNREFUSED",
|
|
3662
|
+
"EPIPE",
|
|
3663
|
+
"ETIMEDOUT",
|
|
3664
|
+
"ENOTFOUND",
|
|
3665
|
+
"EHOSTUNREACH",
|
|
3666
|
+
"ENETUNREACH",
|
|
3667
|
+
"ECONNABORTED",
|
|
3668
|
+
"CONNECTION_ENDED",
|
|
3669
|
+
"CONNECTION_CLOSED",
|
|
3670
|
+
// PostgreSQL SQLSTATE class 08xxx (connection exception) + admin shutdowns
|
|
3671
|
+
"08000",
|
|
3672
|
+
"08001",
|
|
3673
|
+
"08003",
|
|
3674
|
+
"08004",
|
|
3675
|
+
"08006",
|
|
3676
|
+
"08007",
|
|
3677
|
+
"08P01",
|
|
3678
|
+
"57P01",
|
|
3679
|
+
"57P02",
|
|
3680
|
+
"57P03"
|
|
3681
|
+
]);
|
|
3682
|
+
var CONNECTION_ERROR_MESSAGE_RE = /connection (terminated|ended|closed|destroyed|reset|refused|not open)|Connection terminated unexpectedly|Client has encountered a connection error|server closed the connection|Cannot use a pool after calling end|This socket has been ended|connect ECONNRESET|Timeout acquiring a connection/i;
|
|
3652
3683
|
var Db = class _Db {
|
|
3653
3684
|
static async init(context, options = {}) {
|
|
3654
3685
|
const buildConfig = async () => {
|
|
@@ -3896,10 +3927,11 @@ var Db = class _Db {
|
|
|
3896
3927
|
this.knexInstance = null;
|
|
3897
3928
|
this.isConnected = false;
|
|
3898
3929
|
this.queriesLog = [];
|
|
3930
|
+
this._reconnectPromise = null;
|
|
3899
3931
|
this.config = {
|
|
3900
3932
|
testConnection: true,
|
|
3901
3933
|
profile: false,
|
|
3902
|
-
pool: { min:
|
|
3934
|
+
pool: { min: 0, max: 10, idleTimeoutMillis: 3e4 },
|
|
3903
3935
|
acquireConnectionTimeout: 1e4,
|
|
3904
3936
|
ssl: { rejectUnauthorized: false },
|
|
3905
3937
|
logger: console,
|
|
@@ -3917,7 +3949,7 @@ var Db = class _Db {
|
|
|
3917
3949
|
if (!inst.knexInstance) {
|
|
3918
3950
|
throw new Error("Db: Not connected. Call connect() first.");
|
|
3919
3951
|
}
|
|
3920
|
-
return inst.knexInstance(...argumentsList);
|
|
3952
|
+
return inst.wrapQueryBuilder(inst.knexInstance(...argumentsList));
|
|
3921
3953
|
},
|
|
3922
3954
|
get: (target, prop) => {
|
|
3923
3955
|
if (prop === "_instance") {
|
|
@@ -3927,8 +3959,12 @@ var Db = class _Db {
|
|
|
3927
3959
|
const ownMethods = [
|
|
3928
3960
|
"connect",
|
|
3929
3961
|
"disconnect",
|
|
3962
|
+
"reconnect",
|
|
3930
3963
|
"testConnection",
|
|
3931
3964
|
"tableExists",
|
|
3965
|
+
"raw",
|
|
3966
|
+
"withConnectionRetry",
|
|
3967
|
+
"isConnectionError",
|
|
3932
3968
|
"getQueryLog",
|
|
3933
3969
|
"getKnex",
|
|
3934
3970
|
"isConnectedToDb",
|
|
@@ -3948,6 +3984,9 @@ var Db = class _Db {
|
|
|
3948
3984
|
if (inst.knexInstance) {
|
|
3949
3985
|
const knexProp = inst.knexInstance[prop];
|
|
3950
3986
|
if (typeof knexProp === "function") {
|
|
3987
|
+
if (prop === "raw") {
|
|
3988
|
+
return (...args) => inst.raw(...args);
|
|
3989
|
+
}
|
|
3951
3990
|
return knexProp.bind(inst.knexInstance);
|
|
3952
3991
|
}
|
|
3953
3992
|
return knexProp;
|
|
@@ -4027,6 +4066,130 @@ var Db = class _Db {
|
|
|
4027
4066
|
throw error;
|
|
4028
4067
|
}
|
|
4029
4068
|
}
|
|
4069
|
+
/**
|
|
4070
|
+
* True when the error indicates a dead socket / pool that a fresh connect may fix.
|
|
4071
|
+
* Safe to call as `Db.prototype.isConnectionError(err)` or via a connected handle.
|
|
4072
|
+
*/
|
|
4073
|
+
isConnectionError(error) {
|
|
4074
|
+
if (!error) {
|
|
4075
|
+
return false;
|
|
4076
|
+
}
|
|
4077
|
+
if (error instanceof AggregateError && Array.isArray(error.errors)) {
|
|
4078
|
+
return error.errors.some((e) => this.isConnectionError(e));
|
|
4079
|
+
}
|
|
4080
|
+
const code = error.code ?? error.errno;
|
|
4081
|
+
if (code != null && CONNECTION_ERROR_CODES.has(String(code))) {
|
|
4082
|
+
return true;
|
|
4083
|
+
}
|
|
4084
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
4085
|
+
return CONNECTION_ERROR_MESSAGE_RE.test(msg);
|
|
4086
|
+
}
|
|
4087
|
+
/**
|
|
4088
|
+
* Destroy the current knex pool and open a new one. Concurrent callers share one attempt.
|
|
4089
|
+
*/
|
|
4090
|
+
async reconnect() {
|
|
4091
|
+
if (this._reconnectPromise) {
|
|
4092
|
+
await this._reconnectPromise;
|
|
4093
|
+
return;
|
|
4094
|
+
}
|
|
4095
|
+
this._reconnectPromise = (async () => {
|
|
4096
|
+
const old = this.knexInstance;
|
|
4097
|
+
this.isConnected = false;
|
|
4098
|
+
this.knexInstance = null;
|
|
4099
|
+
this.queriesLog = [];
|
|
4100
|
+
if (old) {
|
|
4101
|
+
try {
|
|
4102
|
+
await old.destroy();
|
|
4103
|
+
} catch (error) {
|
|
4104
|
+
this.logger.debug?.(
|
|
4105
|
+
`[Db] destroy during reconnect: ${this.getErrorMessage(error)}`
|
|
4106
|
+
);
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
4109
|
+
await this.connect();
|
|
4110
|
+
})();
|
|
4111
|
+
try {
|
|
4112
|
+
await this._reconnectPromise;
|
|
4113
|
+
} finally {
|
|
4114
|
+
this._reconnectPromise = null;
|
|
4115
|
+
}
|
|
4116
|
+
}
|
|
4117
|
+
async reconnectAfterConnectionError(error) {
|
|
4118
|
+
this.logger.warn?.(
|
|
4119
|
+
`[Db] Connection lost (${this.getErrorMessage(error)}) \u2014 reconnecting\u2026`
|
|
4120
|
+
);
|
|
4121
|
+
await this.reconnect();
|
|
4122
|
+
}
|
|
4123
|
+
/**
|
|
4124
|
+
* Run `fn`; on a connection error, reconnect once (by default) and retry `fn`.
|
|
4125
|
+
* `fn` should look up `this.knexInstance` each call so the retry uses the new client.
|
|
4126
|
+
*/
|
|
4127
|
+
async withConnectionRetry(fn, { retries = 1 } = {}) {
|
|
4128
|
+
let lastError;
|
|
4129
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
4130
|
+
try {
|
|
4131
|
+
return await fn();
|
|
4132
|
+
} catch (error) {
|
|
4133
|
+
lastError = error;
|
|
4134
|
+
if (!this.isConnectionError(error) || attempt >= retries) {
|
|
4135
|
+
throw error;
|
|
4136
|
+
}
|
|
4137
|
+
await this.reconnectAfterConnectionError(error);
|
|
4138
|
+
}
|
|
4139
|
+
}
|
|
4140
|
+
throw lastError;
|
|
4141
|
+
}
|
|
4142
|
+
/**
|
|
4143
|
+
* Wrap a knex QueryBuilder / Raw so that awaiting it retries once after reconnect
|
|
4144
|
+
* when the first attempt dies with a connection error.
|
|
4145
|
+
*/
|
|
4146
|
+
wrapQueryBuilder(builder) {
|
|
4147
|
+
if (!builder || typeof builder.then !== "function" || builder.__dbReconnectWrapped) {
|
|
4148
|
+
return builder;
|
|
4149
|
+
}
|
|
4150
|
+
builder.__dbReconnectWrapped = true;
|
|
4151
|
+
const inst = this;
|
|
4152
|
+
const protoThen = Object.getPrototypeOf(builder)?.then;
|
|
4153
|
+
if (typeof protoThen !== "function") {
|
|
4154
|
+
return builder;
|
|
4155
|
+
}
|
|
4156
|
+
builder.then = function(onFulfilled, onRejected) {
|
|
4157
|
+
const run2 = async () => {
|
|
4158
|
+
try {
|
|
4159
|
+
return await protoThen.call(builder);
|
|
4160
|
+
} catch (error) {
|
|
4161
|
+
if (!inst.isConnectionError(error)) {
|
|
4162
|
+
throw error;
|
|
4163
|
+
}
|
|
4164
|
+
await inst.reconnectAfterConnectionError(error);
|
|
4165
|
+
if (typeof builder.clone === "function") {
|
|
4166
|
+
const retry = builder.clone();
|
|
4167
|
+
retry.client = inst.knexInstance.client;
|
|
4168
|
+
return await protoThen.call(retry);
|
|
4169
|
+
}
|
|
4170
|
+
if (typeof builder.toSQL === "function" && inst.knexInstance) {
|
|
4171
|
+
const sql = builder.toSQL();
|
|
4172
|
+
const statements = Array.isArray(sql) ? sql : [sql];
|
|
4173
|
+
let last;
|
|
4174
|
+
for (const stmt of statements) {
|
|
4175
|
+
last = await inst.knexInstance.raw(stmt.sql, stmt.bindings);
|
|
4176
|
+
}
|
|
4177
|
+
return last;
|
|
4178
|
+
}
|
|
4179
|
+
throw error;
|
|
4180
|
+
}
|
|
4181
|
+
};
|
|
4182
|
+
return run2().then(onFulfilled, onRejected);
|
|
4183
|
+
};
|
|
4184
|
+
return builder;
|
|
4185
|
+
}
|
|
4186
|
+
/** knex.raw with auto-reconnect on dead connections. */
|
|
4187
|
+
raw(...args) {
|
|
4188
|
+
if (!this.knexInstance) {
|
|
4189
|
+
throw new Error("Db: Not connected. Call connect() first.");
|
|
4190
|
+
}
|
|
4191
|
+
return this.wrapQueryBuilder(this.knexInstance.raw(...args));
|
|
4192
|
+
}
|
|
4030
4193
|
getErrorMessage(error) {
|
|
4031
4194
|
if (error instanceof AggregateError) {
|
|
4032
4195
|
const errors = error.errors || [];
|
|
@@ -4197,7 +4360,9 @@ var Db = class _Db {
|
|
|
4197
4360
|
throw new Error("Db: Not connected. Call connect() first.");
|
|
4198
4361
|
}
|
|
4199
4362
|
try {
|
|
4200
|
-
return await this.
|
|
4363
|
+
return await this.withConnectionRetry(
|
|
4364
|
+
() => this.knexInstance.schema.hasTable(tableName)
|
|
4365
|
+
);
|
|
4201
4366
|
} catch (error) {
|
|
4202
4367
|
this.logger.error?.(`[Db] Error checking table existence: ${error.message}`);
|
|
4203
4368
|
throw error;
|
|
@@ -6468,22 +6633,46 @@ async function ensureTaskTables(context, options = {}) {
|
|
|
6468
6633
|
}
|
|
6469
6634
|
}
|
|
6470
6635
|
const { actions } = await ensureSchema(db, spec, { dryRun, logger: context.logger });
|
|
6636
|
+
const legacyDrops = await dropLegacyTaskNameColumn(db, [tasksTable, historyTable], {
|
|
6637
|
+
dryRun,
|
|
6638
|
+
log,
|
|
6639
|
+
label
|
|
6640
|
+
});
|
|
6641
|
+
const allActions = [...actions, ...legacyDrops];
|
|
6471
6642
|
if (dryRun) {
|
|
6472
|
-
if (
|
|
6643
|
+
if (allActions.length === 0) {
|
|
6473
6644
|
log.info?.(`[tasks-schema] dryRun \u2014 ${label}: queue "${queueName}" already up to date; no DDL`);
|
|
6474
6645
|
} else {
|
|
6475
6646
|
log.info?.(
|
|
6476
|
-
`[tasks-schema] dryRun \u2014 ${label}: would run ${
|
|
6647
|
+
`[tasks-schema] dryRun \u2014 ${label}: would run ${allActions.length} statement(s) for queue "${queueName}":`
|
|
6477
6648
|
);
|
|
6478
|
-
for (const s of
|
|
6649
|
+
for (const s of allActions) log.info?.(` - ${s}`);
|
|
6479
6650
|
}
|
|
6480
|
-
} else if (
|
|
6651
|
+
} else if (allActions.length > 0) {
|
|
6481
6652
|
log.info?.(
|
|
6482
|
-
`[tasks-schema] ${label}: applied ${
|
|
6653
|
+
`[tasks-schema] ${label}: applied ${allActions.length} DDL statement(s) for queue "${queueName}"`
|
|
6483
6654
|
);
|
|
6484
6655
|
}
|
|
6485
6656
|
}
|
|
6486
6657
|
}
|
|
6658
|
+
async function dropLegacyTaskNameColumn(db, tableNames, { dryRun, log, label }) {
|
|
6659
|
+
const actions = [];
|
|
6660
|
+
for (const table of tableNames) {
|
|
6661
|
+
if (!await db.tableExists(table).catch(() => false)) continue;
|
|
6662
|
+
const hasTask = await db.schema.hasColumn(table, "task");
|
|
6663
|
+
const hasName = await db.schema.hasColumn(table, "name");
|
|
6664
|
+
if (!hasTask || !hasName) continue;
|
|
6665
|
+
const sql = `ALTER TABLE "${table}" DROP COLUMN IF EXISTS "task"`;
|
|
6666
|
+
actions.push(sql);
|
|
6667
|
+
if (dryRun) {
|
|
6668
|
+
log?.info?.(`[tasks-schema] dryRun \u2014 ${label}: ${sql}`);
|
|
6669
|
+
} else {
|
|
6670
|
+
await db.raw(sql);
|
|
6671
|
+
log?.info?.(`[tasks-schema] ${label}: dropped legacy column ${table}.task`);
|
|
6672
|
+
}
|
|
6673
|
+
}
|
|
6674
|
+
return actions;
|
|
6675
|
+
}
|
|
6487
6676
|
async function enqueueTask(context, options) {
|
|
6488
6677
|
const db = getDb(context);
|
|
6489
6678
|
const queueName = options.queueName ?? "tasks";
|
|
@@ -6500,7 +6689,7 @@ async function enqueueTask(context, options) {
|
|
|
6500
6689
|
} else if (schedule) {
|
|
6501
6690
|
nextRunAt = nextTimeMatch(schedule, /* @__PURE__ */ new Date());
|
|
6502
6691
|
}
|
|
6503
|
-
|
|
6692
|
+
const row = {
|
|
6504
6693
|
id,
|
|
6505
6694
|
name,
|
|
6506
6695
|
params: toJsonColumn(options.params ?? null),
|
|
@@ -6514,9 +6703,21 @@ async function enqueueTask(context, options) {
|
|
|
6514
6703
|
server_name: options.serverName ?? null,
|
|
6515
6704
|
status: "idle",
|
|
6516
6705
|
status_changed_at: db.fn.now()
|
|
6517
|
-
}
|
|
6706
|
+
};
|
|
6707
|
+
if (await tableHasLegacyTaskColumn(db, tasksTable)) {
|
|
6708
|
+
row.task = name;
|
|
6709
|
+
}
|
|
6710
|
+
await db(tasksTable).insert(row);
|
|
6518
6711
|
return id;
|
|
6519
6712
|
}
|
|
6713
|
+
var legacyTaskColumnCache = /* @__PURE__ */ new Map();
|
|
6714
|
+
async function tableHasLegacyTaskColumn(db, tableName) {
|
|
6715
|
+
const key = `${db?.config?.name ?? "db"}:${tableName}`;
|
|
6716
|
+
if (!legacyTaskColumnCache.has(key)) {
|
|
6717
|
+
legacyTaskColumnCache.set(key, await db.schema.hasColumn(tableName, "task"));
|
|
6718
|
+
}
|
|
6719
|
+
return legacyTaskColumnCache.get(key);
|
|
6720
|
+
}
|
|
6520
6721
|
async function updateTaskProgress(context, tasksTable, taskId, progress) {
|
|
6521
6722
|
const db = getDb(context);
|
|
6522
6723
|
await db(tasksTable).where({ id: taskId }).update({
|