@nmakarov/cli-toolkit 0.44.0 → 0.47.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/args.cjs +17 -24
- package/dist/args.cjs.map +1 -1
- package/dist/args.js +17 -24
- package/dist/args.js.map +1 -1
- package/dist/cli-runner.cjs +180 -28
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +180 -28
- 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/deploy.cjs +2 -0
- package/dist/deploy.cjs.map +1 -1
- package/dist/deploy.js +2 -0
- package/dist/deploy.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 +182 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +182 -28
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +17 -24
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +17 -24
- package/dist/init.js.map +1 -1
- package/dist/s3.cjs.map +1 -1
- package/dist/s3.js.map +1 -1
- package/package.json +1 -1
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":"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","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
|
@@ -1561,36 +1561,29 @@ var Args = class _Args {
|
|
|
1561
1561
|
}
|
|
1562
1562
|
return;
|
|
1563
1563
|
}
|
|
1564
|
-
let dotEnvPathFile =
|
|
1565
|
-
|
|
1566
|
-
const envSpecificPath = (0, import_path.resolve)(dotEnvPath, envSpecificFile);
|
|
1567
|
-
if ((0, import_fs.existsSync)(envSpecificPath)) {
|
|
1568
|
-
dotEnvPathFile = envSpecificPath;
|
|
1569
|
-
}
|
|
1570
|
-
if (!dotEnvPathFile && !this.get("dotEnvPath")) {
|
|
1564
|
+
let dotEnvPathFile = (0, import_path.resolve)(dotEnvPath, dotEnvFile);
|
|
1565
|
+
if (!(0, import_fs.existsSync)(dotEnvPathFile) && !this.get("dotEnvPath")) {
|
|
1571
1566
|
const examplesPath = (0, import_path.resolve)(dotEnvPath, "examples");
|
|
1572
|
-
const
|
|
1573
|
-
if ((0, import_fs.existsSync)(
|
|
1574
|
-
dotEnvPathFile =
|
|
1575
|
-
}
|
|
1576
|
-
|
|
1577
|
-
if (!dotEnvPathFile) {
|
|
1578
|
-
dotEnvPathFile = (0, import_path.resolve)(dotEnvPath, dotEnvFile);
|
|
1579
|
-
if (!(0, import_fs.existsSync)(dotEnvPathFile)) {
|
|
1580
|
-
if (!this.get("dotEnvPath")) {
|
|
1581
|
-
const examplesPath = (0, import_path.resolve)(dotEnvPath, "examples");
|
|
1582
|
-
const examplesEnvFile = (0, import_path.resolve)(examplesPath, dotEnvFile);
|
|
1583
|
-
if ((0, import_fs.existsSync)(examplesEnvFile)) {
|
|
1584
|
-
dotEnvPathFile = examplesEnvFile;
|
|
1585
|
-
} else {
|
|
1586
|
-
dotEnvPathFile = (0, import_path.resolve)(dotEnvPath, "..", dotEnvFile);
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1567
|
+
const examplesEnvFile = (0, import_path.resolve)(examplesPath, dotEnvFile);
|
|
1568
|
+
if ((0, import_fs.existsSync)(examplesEnvFile)) {
|
|
1569
|
+
dotEnvPathFile = examplesEnvFile;
|
|
1570
|
+
} else {
|
|
1571
|
+
dotEnvPathFile = (0, import_path.resolve)(dotEnvPath, "..", dotEnvFile);
|
|
1589
1572
|
}
|
|
1590
1573
|
}
|
|
1591
1574
|
if (dotEnvPathFile && (0, import_fs.existsSync)(dotEnvPathFile)) {
|
|
1592
1575
|
(0, import_dotenv.config)({ path: dotEnvPathFile, quiet: true });
|
|
1593
1576
|
}
|
|
1577
|
+
const envSpecificFile = `.env.${this.env}`;
|
|
1578
|
+
const envSpecificPath = (0, import_path.resolve)(dotEnvPath, envSpecificFile);
|
|
1579
|
+
if ((0, import_fs.existsSync)(envSpecificPath) && envSpecificPath !== dotEnvPathFile) {
|
|
1580
|
+
(0, import_dotenv.config)({ path: envSpecificPath, quiet: true });
|
|
1581
|
+
} else if (!this.get("dotEnvPath")) {
|
|
1582
|
+
const examplesOverlay = (0, import_path.resolve)(dotEnvPath, "examples", envSpecificFile);
|
|
1583
|
+
if ((0, import_fs.existsSync)(examplesOverlay)) {
|
|
1584
|
+
(0, import_dotenv.config)({ path: examplesOverlay, quiet: true });
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1594
1587
|
}
|
|
1595
1588
|
/**
|
|
1596
1589
|
* Load configuration files
|
|
@@ -3651,10 +3644,35 @@ async function ensureSchemaEverywhere(dbs, spec, options = {}) {
|
|
|
3651
3644
|
// src/db/index.js
|
|
3652
3645
|
var KNEX_DEFAULTS = {
|
|
3653
3646
|
testConnection: true,
|
|
3654
|
-
|
|
3647
|
+
// min: 0 avoids holding idle sockets that go stale during long-running CLIs
|
|
3648
|
+
pool: { min: 0, max: 10, idleTimeoutMillis: 3e4 },
|
|
3655
3649
|
acquireConnectionTimeout: 1e4,
|
|
3656
3650
|
ssl: { rejectUnauthorized: false }
|
|
3657
3651
|
};
|
|
3652
|
+
var CONNECTION_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
3653
|
+
"ECONNRESET",
|
|
3654
|
+
"ECONNREFUSED",
|
|
3655
|
+
"EPIPE",
|
|
3656
|
+
"ETIMEDOUT",
|
|
3657
|
+
"ENOTFOUND",
|
|
3658
|
+
"EHOSTUNREACH",
|
|
3659
|
+
"ENETUNREACH",
|
|
3660
|
+
"ECONNABORTED",
|
|
3661
|
+
"CONNECTION_ENDED",
|
|
3662
|
+
"CONNECTION_CLOSED",
|
|
3663
|
+
// PostgreSQL SQLSTATE class 08xxx (connection exception) + admin shutdowns
|
|
3664
|
+
"08000",
|
|
3665
|
+
"08001",
|
|
3666
|
+
"08003",
|
|
3667
|
+
"08004",
|
|
3668
|
+
"08006",
|
|
3669
|
+
"08007",
|
|
3670
|
+
"08P01",
|
|
3671
|
+
"57P01",
|
|
3672
|
+
"57P02",
|
|
3673
|
+
"57P03"
|
|
3674
|
+
]);
|
|
3675
|
+
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;
|
|
3658
3676
|
var Db = class _Db {
|
|
3659
3677
|
static async init(context, options = {}) {
|
|
3660
3678
|
const buildConfig = async () => {
|
|
@@ -3902,10 +3920,11 @@ var Db = class _Db {
|
|
|
3902
3920
|
this.knexInstance = null;
|
|
3903
3921
|
this.isConnected = false;
|
|
3904
3922
|
this.queriesLog = [];
|
|
3923
|
+
this._reconnectPromise = null;
|
|
3905
3924
|
this.config = {
|
|
3906
3925
|
testConnection: true,
|
|
3907
3926
|
profile: false,
|
|
3908
|
-
pool: { min:
|
|
3927
|
+
pool: { min: 0, max: 10, idleTimeoutMillis: 3e4 },
|
|
3909
3928
|
acquireConnectionTimeout: 1e4,
|
|
3910
3929
|
ssl: { rejectUnauthorized: false },
|
|
3911
3930
|
logger: console,
|
|
@@ -3923,7 +3942,7 @@ var Db = class _Db {
|
|
|
3923
3942
|
if (!inst.knexInstance) {
|
|
3924
3943
|
throw new Error("Db: Not connected. Call connect() first.");
|
|
3925
3944
|
}
|
|
3926
|
-
return inst.knexInstance(...argumentsList);
|
|
3945
|
+
return inst.wrapQueryBuilder(inst.knexInstance(...argumentsList));
|
|
3927
3946
|
},
|
|
3928
3947
|
get: (target, prop) => {
|
|
3929
3948
|
if (prop === "_instance") {
|
|
@@ -3933,8 +3952,12 @@ var Db = class _Db {
|
|
|
3933
3952
|
const ownMethods = [
|
|
3934
3953
|
"connect",
|
|
3935
3954
|
"disconnect",
|
|
3955
|
+
"reconnect",
|
|
3936
3956
|
"testConnection",
|
|
3937
3957
|
"tableExists",
|
|
3958
|
+
"raw",
|
|
3959
|
+
"withConnectionRetry",
|
|
3960
|
+
"isConnectionError",
|
|
3938
3961
|
"getQueryLog",
|
|
3939
3962
|
"getKnex",
|
|
3940
3963
|
"isConnectedToDb",
|
|
@@ -3954,6 +3977,9 @@ var Db = class _Db {
|
|
|
3954
3977
|
if (inst.knexInstance) {
|
|
3955
3978
|
const knexProp = inst.knexInstance[prop];
|
|
3956
3979
|
if (typeof knexProp === "function") {
|
|
3980
|
+
if (prop === "raw") {
|
|
3981
|
+
return (...args) => inst.raw(...args);
|
|
3982
|
+
}
|
|
3957
3983
|
return knexProp.bind(inst.knexInstance);
|
|
3958
3984
|
}
|
|
3959
3985
|
return knexProp;
|
|
@@ -4033,6 +4059,130 @@ var Db = class _Db {
|
|
|
4033
4059
|
throw error;
|
|
4034
4060
|
}
|
|
4035
4061
|
}
|
|
4062
|
+
/**
|
|
4063
|
+
* True when the error indicates a dead socket / pool that a fresh connect may fix.
|
|
4064
|
+
* Safe to call as `Db.prototype.isConnectionError(err)` or via a connected handle.
|
|
4065
|
+
*/
|
|
4066
|
+
isConnectionError(error) {
|
|
4067
|
+
if (!error) {
|
|
4068
|
+
return false;
|
|
4069
|
+
}
|
|
4070
|
+
if (error instanceof AggregateError && Array.isArray(error.errors)) {
|
|
4071
|
+
return error.errors.some((e) => this.isConnectionError(e));
|
|
4072
|
+
}
|
|
4073
|
+
const code = error.code ?? error.errno;
|
|
4074
|
+
if (code != null && CONNECTION_ERROR_CODES.has(String(code))) {
|
|
4075
|
+
return true;
|
|
4076
|
+
}
|
|
4077
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
4078
|
+
return CONNECTION_ERROR_MESSAGE_RE.test(msg);
|
|
4079
|
+
}
|
|
4080
|
+
/**
|
|
4081
|
+
* Destroy the current knex pool and open a new one. Concurrent callers share one attempt.
|
|
4082
|
+
*/
|
|
4083
|
+
async reconnect() {
|
|
4084
|
+
if (this._reconnectPromise) {
|
|
4085
|
+
await this._reconnectPromise;
|
|
4086
|
+
return;
|
|
4087
|
+
}
|
|
4088
|
+
this._reconnectPromise = (async () => {
|
|
4089
|
+
const old = this.knexInstance;
|
|
4090
|
+
this.isConnected = false;
|
|
4091
|
+
this.knexInstance = null;
|
|
4092
|
+
this.queriesLog = [];
|
|
4093
|
+
if (old) {
|
|
4094
|
+
try {
|
|
4095
|
+
await old.destroy();
|
|
4096
|
+
} catch (error) {
|
|
4097
|
+
this.logger.debug?.(
|
|
4098
|
+
`[Db] destroy during reconnect: ${this.getErrorMessage(error)}`
|
|
4099
|
+
);
|
|
4100
|
+
}
|
|
4101
|
+
}
|
|
4102
|
+
await this.connect();
|
|
4103
|
+
})();
|
|
4104
|
+
try {
|
|
4105
|
+
await this._reconnectPromise;
|
|
4106
|
+
} finally {
|
|
4107
|
+
this._reconnectPromise = null;
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
async reconnectAfterConnectionError(error) {
|
|
4111
|
+
this.logger.warn?.(
|
|
4112
|
+
`[Db] Connection lost (${this.getErrorMessage(error)}) \u2014 reconnecting\u2026`
|
|
4113
|
+
);
|
|
4114
|
+
await this.reconnect();
|
|
4115
|
+
}
|
|
4116
|
+
/**
|
|
4117
|
+
* Run `fn`; on a connection error, reconnect once (by default) and retry `fn`.
|
|
4118
|
+
* `fn` should look up `this.knexInstance` each call so the retry uses the new client.
|
|
4119
|
+
*/
|
|
4120
|
+
async withConnectionRetry(fn, { retries = 1 } = {}) {
|
|
4121
|
+
let lastError;
|
|
4122
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
4123
|
+
try {
|
|
4124
|
+
return await fn();
|
|
4125
|
+
} catch (error) {
|
|
4126
|
+
lastError = error;
|
|
4127
|
+
if (!this.isConnectionError(error) || attempt >= retries) {
|
|
4128
|
+
throw error;
|
|
4129
|
+
}
|
|
4130
|
+
await this.reconnectAfterConnectionError(error);
|
|
4131
|
+
}
|
|
4132
|
+
}
|
|
4133
|
+
throw lastError;
|
|
4134
|
+
}
|
|
4135
|
+
/**
|
|
4136
|
+
* Wrap a knex QueryBuilder / Raw so that awaiting it retries once after reconnect
|
|
4137
|
+
* when the first attempt dies with a connection error.
|
|
4138
|
+
*/
|
|
4139
|
+
wrapQueryBuilder(builder) {
|
|
4140
|
+
if (!builder || typeof builder.then !== "function" || builder.__dbReconnectWrapped) {
|
|
4141
|
+
return builder;
|
|
4142
|
+
}
|
|
4143
|
+
builder.__dbReconnectWrapped = true;
|
|
4144
|
+
const inst = this;
|
|
4145
|
+
const protoThen = Object.getPrototypeOf(builder)?.then;
|
|
4146
|
+
if (typeof protoThen !== "function") {
|
|
4147
|
+
return builder;
|
|
4148
|
+
}
|
|
4149
|
+
builder.then = function(onFulfilled, onRejected) {
|
|
4150
|
+
const run2 = async () => {
|
|
4151
|
+
try {
|
|
4152
|
+
return await protoThen.call(builder);
|
|
4153
|
+
} catch (error) {
|
|
4154
|
+
if (!inst.isConnectionError(error)) {
|
|
4155
|
+
throw error;
|
|
4156
|
+
}
|
|
4157
|
+
await inst.reconnectAfterConnectionError(error);
|
|
4158
|
+
if (typeof builder.clone === "function") {
|
|
4159
|
+
const retry = builder.clone();
|
|
4160
|
+
retry.client = inst.knexInstance.client;
|
|
4161
|
+
return await protoThen.call(retry);
|
|
4162
|
+
}
|
|
4163
|
+
if (typeof builder.toSQL === "function" && inst.knexInstance) {
|
|
4164
|
+
const sql = builder.toSQL();
|
|
4165
|
+
const statements = Array.isArray(sql) ? sql : [sql];
|
|
4166
|
+
let last;
|
|
4167
|
+
for (const stmt of statements) {
|
|
4168
|
+
last = await inst.knexInstance.raw(stmt.sql, stmt.bindings);
|
|
4169
|
+
}
|
|
4170
|
+
return last;
|
|
4171
|
+
}
|
|
4172
|
+
throw error;
|
|
4173
|
+
}
|
|
4174
|
+
};
|
|
4175
|
+
return run2().then(onFulfilled, onRejected);
|
|
4176
|
+
};
|
|
4177
|
+
return builder;
|
|
4178
|
+
}
|
|
4179
|
+
/** knex.raw with auto-reconnect on dead connections. */
|
|
4180
|
+
raw(...args) {
|
|
4181
|
+
if (!this.knexInstance) {
|
|
4182
|
+
throw new Error("Db: Not connected. Call connect() first.");
|
|
4183
|
+
}
|
|
4184
|
+
return this.wrapQueryBuilder(this.knexInstance.raw(...args));
|
|
4185
|
+
}
|
|
4036
4186
|
getErrorMessage(error) {
|
|
4037
4187
|
if (error instanceof AggregateError) {
|
|
4038
4188
|
const errors = error.errors || [];
|
|
@@ -4203,7 +4353,9 @@ var Db = class _Db {
|
|
|
4203
4353
|
throw new Error("Db: Not connected. Call connect() first.");
|
|
4204
4354
|
}
|
|
4205
4355
|
try {
|
|
4206
|
-
return await this.
|
|
4356
|
+
return await this.withConnectionRetry(
|
|
4357
|
+
() => this.knexInstance.schema.hasTable(tableName)
|
|
4358
|
+
);
|
|
4207
4359
|
} catch (error) {
|
|
4208
4360
|
this.logger.error?.(`[Db] Error checking table existence: ${error.message}`);
|
|
4209
4361
|
throw error;
|
|
@@ -5812,6 +5964,8 @@ module.exports = {
|
|
|
5812
5964
|
merge_logs: true,
|
|
5813
5965
|
time: true,
|
|
5814
5966
|
env: {
|
|
5967
|
+
// cli-toolkit Args: ENV selects env-suffixed defaults (DB_NAME_PRODUCTION, BUCKET_PRODUCTION, \u2026)
|
|
5968
|
+
ENV: "production",
|
|
5815
5969
|
NODE_ENV: "production",
|
|
5816
5970
|
},
|
|
5817
5971
|
},
|