mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
fix(lint): resolve all ESLint warnings and errors in Next.js frontend
- next.config.ts: remove non-null assertions, type webpack param properly - bootstrap/route.ts: simplify null check to != null - workflows/route.ts: fix nullable string conditional, remove unnecessary ?? - ExecutionMonitor.tsx: add braces to void-returning arrow functions - WorkflowBuilder.tsx: explicit null checks for nullable objects - package-utils.ts: remove unnecessary ??, explicit null check - fetch-session.ts, login.ts, get-current-user.ts: remove unnecessary ?. chains - db-client.ts: explicit null checks - error-reporting.ts: type ErrorCategory properly - multi-tenant-context.examples.ts: remove await of non-Promise, prefer-optional-chain - multi-tenant-context.ts: nullable boolean ==> === true, remove unused eslint-disable, remove async from bindCredentials, restore executionLimits fallback to getDefaultExecutionLimits() - workflow-error-handler.ts: remove unnecessary ?? fallbacks - workflow-loader-v2.ts: remove unused eslint-disable, use ??= assignment - store.ts: remove unnecessary type assertions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type { NextConfig } from 'next'
|
||||
import type { Configuration } from 'webpack'
|
||||
import type webpack from 'webpack'
|
||||
import path from 'path'
|
||||
|
||||
const projectDir = process.cwd()
|
||||
@@ -101,12 +102,13 @@ const nextConfig: NextConfig = {
|
||||
'@dbal-ui': path.resolve(projectDir, '../../dbal/shared/ui'),
|
||||
},
|
||||
},
|
||||
webpack(config: Configuration, { isServer, webpack }) {
|
||||
webpack(config: Configuration, { isServer, webpack: wp }: { isServer: boolean; webpack: typeof webpack }) {
|
||||
// Stub ALL external SCSS module imports with an actual .module.scss
|
||||
// so they go through the CSS module pipeline (css-loader sets .default correctly)
|
||||
const stubScss = path.resolve(projectDir, 'src/lib/empty.module.scss')
|
||||
config.plugins!.push(
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
config.plugins ??= []
|
||||
config.plugins.push(
|
||||
new wp.NormalModuleReplacementPlugin(
|
||||
/\.module\.scss$/,
|
||||
function (resource: { context?: string; request?: string }) {
|
||||
const ctx = resource.context ?? ''
|
||||
@@ -116,30 +118,36 @@ const nextConfig: NextConfig = {
|
||||
}
|
||||
)
|
||||
)
|
||||
config.optimization!.minimize = false
|
||||
|
||||
config.resolve!.alias = {
|
||||
...(config.resolve!.alias as Record<string, string>),
|
||||
'@metabuilder/components': path.resolve(projectDir, 'src/lib/components-shim.ts'),
|
||||
'@dbal-ui': path.resolve(projectDir, '../../dbal/shared/ui'),
|
||||
// Resolve service-adapters to source (dist/ is not pre-built)
|
||||
'@metabuilder/service-adapters': path.resolve(monorepoRoot, 'redux/adapters/src'),
|
||||
if (config.optimization != null) {
|
||||
config.optimization.minimize = false
|
||||
}
|
||||
|
||||
config.externals = [...((config.externals as string[]) ?? []), 'esbuild']
|
||||
if (config.resolve != null) {
|
||||
config.resolve.alias = {
|
||||
...(config.resolve.alias as Record<string, string>),
|
||||
'@metabuilder/components': path.resolve(projectDir, 'src/lib/components-shim.ts'),
|
||||
'@dbal-ui': path.resolve(projectDir, '../../dbal/shared/ui'),
|
||||
// Resolve service-adapters to source (dist/ is not pre-built)
|
||||
'@metabuilder/service-adapters': path.resolve(monorepoRoot, 'redux/adapters/src'),
|
||||
}
|
||||
}
|
||||
|
||||
config.externals = [...(config.externals as string[]), 'esbuild']
|
||||
|
||||
if (!isServer) {
|
||||
config.resolve!.fallback = {
|
||||
...(config.resolve!.fallback as Record<string, false>),
|
||||
'@aws-sdk/client-s3': false,
|
||||
fs: false,
|
||||
path: false,
|
||||
crypto: false,
|
||||
stream: false,
|
||||
'stream/promises': false,
|
||||
os: false,
|
||||
buffer: false,
|
||||
util: false,
|
||||
if (config.resolve != null) {
|
||||
config.resolve.fallback = {
|
||||
...(config.resolve.fallback as Record<string, false>),
|
||||
'@aws-sdk/client-s3': false,
|
||||
fs: false,
|
||||
path: false,
|
||||
crypto: false,
|
||||
stream: false,
|
||||
'stream/promises': false,
|
||||
os: false,
|
||||
buffer: false,
|
||||
util: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ async function dbalPost(entity: string, data: Record<string, unknown>): Promise<
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const limitResponse = applyRateLimit(request, 'bootstrap')
|
||||
if (limitResponse !== null && limitResponse !== undefined) {
|
||||
if (limitResponse != null) {
|
||||
return limitResponse
|
||||
}
|
||||
|
||||
|
||||
@@ -117,9 +117,8 @@ export async function GET(
|
||||
const offset = parseInt(searchParams.get('offset') ?? '0')
|
||||
const category = searchParams.get('category')
|
||||
const tags = searchParams.get('tags')?.split(',').map((t) => t.trim())
|
||||
const active = searchParams.get('active')
|
||||
? searchParams.get('active') === 'true'
|
||||
: undefined
|
||||
const activeParam = searchParams.get('active')
|
||||
const active = activeParam != null ? activeParam === 'true' : undefined
|
||||
|
||||
// 6. Build filter
|
||||
const filter: Record<string, unknown> = {
|
||||
@@ -286,9 +285,9 @@ export async function POST(
|
||||
notificationChannels: [],
|
||||
},
|
||||
nodes: Array.isArray(body.nodes) ? (body.nodes as unknown[]) : [],
|
||||
connections: (body.connections as Record<string, unknown>) ?? {},
|
||||
connections: (body.connections as Record<string, unknown>),
|
||||
triggers: Array.isArray(body.triggers) ? (body.triggers as unknown[]) : [],
|
||||
variables: (body.variables as Record<string, unknown>) ?? {},
|
||||
variables: (body.variables as Record<string, unknown>),
|
||||
errorHandling: {
|
||||
default: 'stopWorkflow' as const,
|
||||
errorNotification: false,
|
||||
@@ -309,7 +308,7 @@ export async function POST(
|
||||
onLimitExceeded: 'reject' as const,
|
||||
},
|
||||
credentials: [],
|
||||
metadata: (body.metadata as Record<string, unknown>) ?? {},
|
||||
metadata: (body.metadata as Record<string, unknown>),
|
||||
executionLimits: {
|
||||
maxExecutionTime: 300000,
|
||||
maxMemoryMb: 512,
|
||||
|
||||
@@ -121,7 +121,7 @@ export const ExecutionMonitor: React.FC<ExecutionMonitorProps> = ({
|
||||
key={execution.id}
|
||||
execution={execution}
|
||||
isSelected={selectedExecutionId === execution.id}
|
||||
onClick={() => handleExecutionSelect(execution.id)}
|
||||
onClick={() => { handleExecutionSelect(execution.id) }}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -146,11 +146,11 @@ export const ExecutionMonitor: React.FC<ExecutionMonitorProps> = ({
|
||||
nodeId={nodeId}
|
||||
result={result}
|
||||
isExpanded={expandedNodeId === nodeId}
|
||||
onToggle={() =>
|
||||
onToggle={() => {
|
||||
setExpandedNodeId(
|
||||
expandedNodeId === nodeId ? null : nodeId
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -420,7 +420,7 @@ const LogViewer: React.FC<LogViewerProps> = ({ logs }) => {
|
||||
<button
|
||||
key={level}
|
||||
className={`${styles.filterButton} ${filter === level ? styles.active : ''}`}
|
||||
onClick={() => setFilter(level)}
|
||||
onClick={() => { setFilter(level) }}
|
||||
>
|
||||
{level}
|
||||
</button>
|
||||
|
||||
@@ -167,7 +167,7 @@ export const WorkflowBuilder: React.FC<WorkflowBuilderProps> = ({
|
||||
<input
|
||||
type="text"
|
||||
placeholder={variable.description ?? name}
|
||||
value={(triggerData[name] as string) ?? ''}
|
||||
value={(triggerData[name] as string)}
|
||||
onChange={(e) => {
|
||||
handleTriggerDataChange(name, e.target.value)
|
||||
}}
|
||||
@@ -344,13 +344,13 @@ function renderConnections(workflow: WorkflowDefinition) {
|
||||
|
||||
Object.entries(workflow.connections).forEach(([fromNodeId, portMap]: [string, Record<string, Record<string, ConnectionTarget[]>>]) => {
|
||||
const fromNode = workflow.nodes.find((n) => n.id === fromNodeId)
|
||||
if (!fromNode) return
|
||||
if (fromNode == null) return
|
||||
|
||||
Object.entries(portMap).forEach(([_portName, indexMap]: [string, Record<string, ConnectionTarget[]>]) => {
|
||||
Object.entries(indexMap).forEach(([_, targets]: [string, ConnectionTarget[]]) => {
|
||||
targets.forEach((target: ConnectionTarget) => {
|
||||
const toNode = workflow.nodes.find((n) => n.id === target.node)
|
||||
if (!toNode) return
|
||||
if (toNode == null) return
|
||||
|
||||
const [x1, y1] = fromNode.position
|
||||
const [x2, y2] = toNode.position
|
||||
|
||||
@@ -82,7 +82,7 @@ export function getErrorMessage(error: PackageError | Error | null): string {
|
||||
return messages[error.code] ?? error.message
|
||||
}
|
||||
|
||||
return error.message ?? 'An unknown error occurred'
|
||||
return error.message
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,7 +135,7 @@ export function formatVersion(version: string): string {
|
||||
// Ensure version matches semver format
|
||||
const semverRegex = /^\d+\.\d+\.\d+/
|
||||
const match = version.match(semverRegex)
|
||||
return match ? match[0] : version
|
||||
return match != null ? match[0] : version
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,7 +28,7 @@ export async function fetchSession(): Promise<User | null> {
|
||||
filter: { token: sessionToken }
|
||||
})
|
||||
|
||||
const session = sessions.data?.[0] as DbalSessionRecord | undefined
|
||||
const session = sessions.data[0] as DbalSessionRecord | undefined
|
||||
|
||||
if (session === undefined) {
|
||||
return null
|
||||
|
||||
@@ -45,7 +45,7 @@ export async function login(identifier: string, password: string): Promise<Login
|
||||
}
|
||||
})
|
||||
|
||||
let user = users.data?.[0] as DbalUserRecord | undefined
|
||||
let user = users.data[0] as DbalUserRecord | undefined
|
||||
|
||||
// If not found by username, try email
|
||||
if (user === undefined) {
|
||||
@@ -54,7 +54,7 @@ export async function login(identifier: string, password: string): Promise<Login
|
||||
email: identifier
|
||||
}
|
||||
})
|
||||
user = usersByEmail.data?.[0] as DbalUserRecord | undefined
|
||||
user = usersByEmail.data[0] as DbalUserRecord | undefined
|
||||
}
|
||||
|
||||
if (user === undefined) {
|
||||
|
||||
@@ -26,7 +26,7 @@ export async function getCurrentUser(): Promise<CurrentUser | null> {
|
||||
const cookieStore = await cookies()
|
||||
const sessionToken = cookieStore.get(SESSION_COOKIE)
|
||||
|
||||
if (!sessionToken?.value || sessionToken.value.length === 0) {
|
||||
if (sessionToken?.value == null || sessionToken.value.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export async function getCurrentUser(): Promise<CurrentUser | null> {
|
||||
filter: { token: sessionToken.value }
|
||||
})
|
||||
|
||||
const session = sessions.data?.[0] as DbalSessionRecord | undefined
|
||||
const session = sessions.data[0] as DbalSessionRecord | undefined
|
||||
|
||||
if (session == null) {
|
||||
return null
|
||||
|
||||
@@ -109,7 +109,7 @@ function createOps(entityName: string): EntityOps {
|
||||
return {
|
||||
async list(options?: ListOptions): Promise<ListResult> {
|
||||
const params = new URLSearchParams()
|
||||
if (options?.filter) {
|
||||
if (options?.filter != null) {
|
||||
for (const [k, v] of Object.entries(options.filter)) {
|
||||
if (v !== undefined && v !== null) params.set(k, String(v as string | number | boolean))
|
||||
}
|
||||
@@ -127,7 +127,7 @@ function createOps(entityName: string): EntityOps {
|
||||
if (Array.isArray(payload)) {
|
||||
return { data: payload, total: payload.length }
|
||||
}
|
||||
if (payload != null && Array.isArray(payload.data)) {
|
||||
if (Array.isArray(payload.data)) {
|
||||
return { data: payload.data as Record<string, unknown>[], total: payload.total as number | undefined }
|
||||
}
|
||||
return { data: [] }
|
||||
|
||||
@@ -161,7 +161,7 @@ class ErrorReportingService {
|
||||
|
||||
// suggestedAction reflects the current category, even if mutated after creation
|
||||
Object.defineProperty(report, 'suggestedAction', {
|
||||
get() { return getSuggestedAction(this.category) },
|
||||
get(this: ErrorReport) { return getSuggestedAction(this.category) },
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
})
|
||||
|
||||
@@ -332,7 +332,7 @@ export async function executeScheduledWorkflow(
|
||||
})
|
||||
|
||||
// 2. Get schedule configuration
|
||||
const trigger = workflow.triggers?.find((t) => t.kind === 'schedule')
|
||||
const trigger = workflow.triggers.find((t) => t.kind === 'schedule')
|
||||
const cronExpression = trigger?.schedule ?? '0 */6 * * *'
|
||||
|
||||
const context = await builder.build(
|
||||
@@ -430,7 +430,7 @@ export async function validateWorkflowExecution(req: NextRequest) {
|
||||
}
|
||||
|
||||
const builder = new MultiTenantContextBuilder(workflow, requestContext)
|
||||
const result = await builder.validate()
|
||||
const result = builder.validate()
|
||||
|
||||
// Return validation result for UI
|
||||
return NextResponse.json({
|
||||
@@ -559,8 +559,8 @@ export async function logExecutionContext(context: ExtendedWorkflowContext) {
|
||||
workflow: sanitized.workflowId,
|
||||
tenant: sanitized.tenantId,
|
||||
user: sanitized.userId,
|
||||
mode: (sanitized.multiTenant as Record<string, unknown>)?.executionMode,
|
||||
timestamp: (sanitized.multiTenant as Record<string, unknown>)?.requestedAt,
|
||||
mode: (sanitized.multiTenant as Record<string, unknown>).executionMode,
|
||||
timestamp: (sanitized.multiTenant as Record<string, unknown>).requestedAt,
|
||||
// Variables listed as keys only, no values
|
||||
variables: sanitized.variables,
|
||||
// Limits included for monitoring
|
||||
@@ -657,7 +657,7 @@ export async function retryFailedWorkflowExecution(
|
||||
async function verifyUserAuth(req: NextRequest): Promise<AuthenticatedUser | null> {
|
||||
// In production, parse JWT and get user details
|
||||
const authHeader = req.headers.get('authorization')
|
||||
if (authHeader === null || !authHeader.startsWith('Bearer ')) {
|
||||
if (authHeader?.startsWith('Bearer ') !== true) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ export class MultiTenantContextBuilder {
|
||||
triggerData: requestData?.triggerData ?? {},
|
||||
variables: this.buildVariables(requestData?.variables),
|
||||
secrets: requestData?.secrets ?? {},
|
||||
request: this.options.captureRequestData ? requestData?.request as WorkflowContext['request'] : undefined,
|
||||
request: this.options.captureRequestData === true ? requestData?.request as WorkflowContext['request'] : undefined,
|
||||
multiTenant: multiTenantMeta,
|
||||
requestMetadata: {
|
||||
ipAddress: this.requestContext.ipAddress,
|
||||
@@ -205,15 +205,15 @@ export class MultiTenantContextBuilder {
|
||||
this.validateContextSafety(context)
|
||||
|
||||
// 5. Load and bind credentials
|
||||
if (this.options.enforceCredentialValidation) {
|
||||
await this.bindCredentials(context)
|
||||
if (this.options.enforceCredentialValidation === true) {
|
||||
this.bindCredentials(context)
|
||||
}
|
||||
|
||||
// 6. Validate variables don't cross tenants
|
||||
this.validateVariableTenantIsolation(context)
|
||||
|
||||
// 7. Log context creation (audit)
|
||||
if (this.options.enableAuditLogging) {
|
||||
if (this.options.enableAuditLogging === true) {
|
||||
this.logContextCreation(context)
|
||||
}
|
||||
|
||||
@@ -231,13 +231,12 @@ export class MultiTenantContextBuilder {
|
||||
|
||||
// Super-admin (level 4) can access any tenant
|
||||
if (this.requestContext.userLevel >= 4) {
|
||||
if (!this.options.allowCrossTenantAccess) {
|
||||
if (this.options.allowCrossTenantAccess !== true) {
|
||||
throw new Error(
|
||||
`Cross-tenant access disabled: User ${this.requestContext.userId} ` +
|
||||
`cannot access workflow in tenant ${this.workflow.tenantId}`
|
||||
)
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`[SECURITY] Super-admin ${this.requestContext.userId} accessing ` +
|
||||
`cross-tenant workflow ${this.workflow.id}`
|
||||
@@ -327,28 +326,24 @@ export class MultiTenantContextBuilder {
|
||||
const variables: DataRecord = {}
|
||||
|
||||
// 1. Add workflow defaults
|
||||
if (this.workflow.variables != null) {
|
||||
for (const [varName, varDef] of Object.entries(this.workflow.variables)) {
|
||||
// Only allow workflow and execution scopes (not global)
|
||||
if (varDef.scope === 'global') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`[SECURITY] Skipping global-scope variable ${varName} - not allowed`)
|
||||
continue
|
||||
}
|
||||
|
||||
variables[varName] = varDef.defaultValue ?? null
|
||||
for (const [varName, varDef] of Object.entries(this.workflow.variables)) {
|
||||
// Only allow workflow and execution scopes (not global)
|
||||
if (varDef.scope === 'global') {
|
||||
console.warn(`[SECURITY] Skipping global-scope variable ${varName} - not allowed`)
|
||||
continue
|
||||
}
|
||||
|
||||
variables[varName] = varDef.defaultValue ?? null
|
||||
}
|
||||
|
||||
// 2. Merge request overrides
|
||||
if (requestVariables != null) {
|
||||
for (const [varName, varValue] of Object.entries(requestVariables)) {
|
||||
// Validate variable is allowed by workflow
|
||||
const varDef = this.workflow.variables?.[varName]
|
||||
const varDef = this.workflow.variables[varName]
|
||||
if (varDef != null) {
|
||||
variables[varName] = varValue
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`[SECURITY] Rejecting unknown variable ${varName} - not in workflow definition`
|
||||
)
|
||||
@@ -371,7 +366,7 @@ export class MultiTenantContextBuilder {
|
||||
const errors: string[] = []
|
||||
|
||||
// 1. Tenant ID must match (unless cross-tenant access is explicitly allowed)
|
||||
if (context.tenantId !== this.workflow.tenantId && !this.options.allowCrossTenantAccess) {
|
||||
if (context.tenantId !== this.workflow.tenantId && this.options.allowCrossTenantAccess !== true) {
|
||||
errors.push(
|
||||
`Context tenant ${context.tenantId} does not match ` +
|
||||
`workflow tenant ${this.workflow.tenantId}`
|
||||
@@ -389,13 +384,12 @@ export class MultiTenantContextBuilder {
|
||||
}
|
||||
|
||||
// 4. Check execution limits
|
||||
if (this.workflow.executionLimits != null) {
|
||||
if (context.executionLimits.maxExecutionTime > this.workflow.executionLimits.maxExecutionTime) {
|
||||
errors.push(
|
||||
`Requested execution time (${String(context.executionLimits.maxExecutionTime)}ms) ` +
|
||||
`exceeds workflow limit (${String(this.workflow.executionLimits.maxExecutionTime)}ms)`
|
||||
)
|
||||
}
|
||||
const workflowLimit = this.workflow.executionLimits ?? this.getDefaultExecutionLimits()
|
||||
if (context.executionLimits.maxExecutionTime > workflowLimit.maxExecutionTime) {
|
||||
errors.push(
|
||||
`Requested execution time (${String(context.executionLimits.maxExecutionTime)}ms) ` +
|
||||
`exceeds workflow limit (${String(workflowLimit.maxExecutionTime)}ms)`
|
||||
)
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
@@ -425,8 +419,8 @@ export class MultiTenantContextBuilder {
|
||||
/**
|
||||
* Load and bind credentials from workflow definition
|
||||
*/
|
||||
private async bindCredentials(context: ExtendedWorkflowContext): Promise<void> {
|
||||
const bindings = this.workflow.credentials ?? []
|
||||
private bindCredentials(context: ExtendedWorkflowContext): void {
|
||||
const bindings = this.workflow.credentials
|
||||
|
||||
for (const binding of bindings) {
|
||||
try {
|
||||
@@ -449,7 +443,6 @@ export class MultiTenantContextBuilder {
|
||||
name: binding.credentialName,
|
||||
})
|
||||
} catch (error: unknown) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Failed to bind credential for node ${binding.nodeId}:`, error)
|
||||
throw error
|
||||
}
|
||||
@@ -535,7 +528,7 @@ export class MultiTenantContextBuilder {
|
||||
}
|
||||
|
||||
// 4. Check for global scope variables
|
||||
for (const [varName, varDef] of Object.entries(this.workflow.variables ?? {})) {
|
||||
for (const [varName, varDef] of Object.entries(this.workflow.variables)) {
|
||||
if (varDef.scope === 'global') {
|
||||
warnings.push({
|
||||
path: `variables.${varName}`,
|
||||
@@ -546,8 +539,8 @@ export class MultiTenantContextBuilder {
|
||||
}
|
||||
|
||||
// 5. Check credentials
|
||||
const credentialCount = this.workflow.credentials?.length ?? 0
|
||||
if (this.options.enforceCredentialValidation && credentialCount > 0) {
|
||||
const credentialCount = this.workflow.credentials.length
|
||||
if (this.options.enforceCredentialValidation === true && credentialCount > 0) {
|
||||
warnings.push({
|
||||
path: 'credentials',
|
||||
message: `${String(credentialCount)} credential(s) will be validated during execution`,
|
||||
|
||||
@@ -387,9 +387,9 @@ export class WorkflowErrorHandler {
|
||||
): WorkflowApiResponse {
|
||||
const code = this.getErrorCode(error)
|
||||
const message = this.isDevelopment
|
||||
? (this.getErrorMessage(error) ?? ERROR_MESSAGES[code])
|
||||
: (ERROR_MESSAGES[code] ?? this.getErrorMessage(error))
|
||||
const statusCode = ERROR_STATUS_MAP[code] ?? 500
|
||||
? this.getErrorMessage(error)
|
||||
: ERROR_MESSAGES[code]
|
||||
const statusCode = ERROR_STATUS_MAP[code]
|
||||
|
||||
const response: FormattedError = {
|
||||
success: false,
|
||||
@@ -462,7 +462,7 @@ export class WorkflowErrorHandler {
|
||||
errorCode: WorkflowErrorCode,
|
||||
context: ErrorContext = {}
|
||||
): WorkflowApiResponse {
|
||||
const statusCode = ERROR_STATUS_MAP[errorCode] ?? 401
|
||||
const statusCode = ERROR_STATUS_MAP[errorCode]
|
||||
|
||||
const response: FormattedError = {
|
||||
success: false,
|
||||
@@ -636,7 +636,7 @@ export class WorkflowErrorHandler {
|
||||
* Get suggestion for validation error
|
||||
*/
|
||||
private getSuggestionForError(error: ValidationError): string {
|
||||
const code = (error.code ?? '').toUpperCase()
|
||||
const code = error.code.toUpperCase()
|
||||
const suggestions: Record<string, string> = {
|
||||
MISSING_REQUIRED_FIELD: 'Add the missing parameter to the node.',
|
||||
INVALID_NODE_TYPE: 'Use a valid node type from the registry.',
|
||||
|
||||
@@ -282,7 +282,6 @@ export class ValidationCache {
|
||||
}
|
||||
|
||||
if (cleaned > 0) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`[CACHE CLEANUP] Removed ${cleaned} expired entries`)
|
||||
}
|
||||
}, 5 * 60 * 1000) // Every 5 minutes
|
||||
@@ -416,7 +415,6 @@ export class WorkflowLoaderV2 {
|
||||
const cached = this.cache.get(cacheKey)
|
||||
if (cached != null) {
|
||||
if (this.enableLogging) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`[CACHE HIT] Validation for workflow ${workflow.id}`)
|
||||
}
|
||||
return {
|
||||
@@ -430,7 +428,6 @@ export class WorkflowLoaderV2 {
|
||||
const existingValidation = this.activeValidations.get(validationKey)
|
||||
if (existingValidation != null) {
|
||||
if (this.enableLogging) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`[DEDUP] Reusing in-flight validation for ${validationKey}`
|
||||
)
|
||||
@@ -475,7 +472,6 @@ export class WorkflowLoaderV2 {
|
||||
workflows: WorkflowDefinition[]
|
||||
): Promise<ExtendedValidationResult[]> {
|
||||
if (this.enableLogging) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`Starting batch validation for ${workflows.length} workflows`)
|
||||
}
|
||||
|
||||
@@ -541,7 +537,6 @@ export class WorkflowLoaderV2 {
|
||||
const cacheKey = `${tenantId}:${workflowId}`
|
||||
this.cache.delete(cacheKey)
|
||||
if (this.enableLogging) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`[CACHE INVALIDATED] ${workflowId}`)
|
||||
}
|
||||
}
|
||||
@@ -596,7 +591,6 @@ export class WorkflowLoaderV2 {
|
||||
clearCache(): void {
|
||||
this.cache.clear()
|
||||
if (this.enableLogging) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('All validation caches cleared')
|
||||
}
|
||||
}
|
||||
@@ -662,7 +656,6 @@ export class WorkflowLoaderV2 {
|
||||
const duration = Date.now() - startTime
|
||||
|
||||
if (this.enableLogging) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`[VALIDATION] Workflow ${workflow.id} validated in ${duration}ms`, {
|
||||
nodeCount: workflow.nodes.length,
|
||||
connectionCount: Object.keys(workflow.connections).length,
|
||||
@@ -869,9 +862,7 @@ let globalLoader: WorkflowLoaderV2 | null = null
|
||||
export function getWorkflowLoader(
|
||||
options?: WorkflowLoaderV2Options
|
||||
): WorkflowLoaderV2 {
|
||||
if (globalLoader == null) {
|
||||
globalLoader = new WorkflowLoaderV2(options)
|
||||
}
|
||||
globalLoader ??= new WorkflowLoaderV2(options)
|
||||
return globalLoader
|
||||
}
|
||||
|
||||
|
||||
@@ -46,14 +46,14 @@ const { store, persistor } = createPersistedStore({
|
||||
middleware: (base: Middleware[]) => {
|
||||
const middlewares: Middleware[] = [...base]
|
||||
if (isDev) {
|
||||
middlewares.push(createLoggingMiddleware({ verbose: false }) as Middleware)
|
||||
middlewares.push(createPerformanceMiddleware() as Middleware)
|
||||
middlewares.push(createLoggingMiddleware({ verbose: false }))
|
||||
middlewares.push(createPerformanceMiddleware())
|
||||
}
|
||||
middlewares.push(createAnalyticsMiddleware() as Middleware)
|
||||
middlewares.push(createErrorMiddleware() as Middleware)
|
||||
middlewares.push(createAnalyticsMiddleware())
|
||||
middlewares.push(createErrorMiddleware())
|
||||
return middlewares
|
||||
},
|
||||
devTools: getDevToolsConfig(),
|
||||
devTools: getDevToolsConfig() as boolean | object,
|
||||
ignoredActions: ['asyncData/fetchAsyncData/pending'],
|
||||
ignoredPaths: ['asyncData.requests.*.promise'],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user