Skip to content

Commit 3b9152f

Browse files
committed
fix(export): address code review feedback - add proper TypeScript types
- Replace 'any' types with proper interfaces (WorkflowBlock, WorkflowState, ExportWorkflowState, WorkflowVariable) - Improve transpiler regex pattern to explicitly handle string and numeric bracket notation - Add documentation for regex limitations with nested bracket access - Use nullish coalescing (??) instead of logical OR (||) for safer defaults
1 parent 0013c6b commit 3b9152f

File tree

1 file changed

+57
-12
lines changed
  • apps/sim/app/api/workflows/[id]/export-service

1 file changed

+57
-12
lines changed

apps/sim/app/api/workflows/[id]/export-service/route.ts

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,38 +45,58 @@ interface ValidationResult {
4545
message: string
4646
}
4747

48+
// Type for workflow block during validation
49+
interface WorkflowBlock {
50+
type: string
51+
name?: string
52+
subBlocks?: {
53+
model?: { value?: string }
54+
[key: string]: unknown
55+
}
56+
inputs?: {
57+
model?: string
58+
[key: string]: unknown
59+
}
60+
}
61+
62+
// Type for workflow state
63+
interface WorkflowState {
64+
blocks?: Record<string, WorkflowBlock>
65+
edges?: Record<string, unknown>
66+
[key: string]: unknown
67+
}
68+
4869
/**
4970
* Validate workflow for export compatibility.
5071
* Checks for unsupported block types and providers.
5172
*/
52-
function validateWorkflowForExport(state: any): ValidationResult {
73+
function validateWorkflowForExport(state: WorkflowState | null | undefined): ValidationResult {
5374
const unsupportedBlocks: Array<{ id: string; name: string; type: string }> = []
5475
const unsupportedProviders: Array<{ id: string; name: string; model: string; provider: string }> = []
5576

56-
const blocks = state?.blocks || {}
77+
const blocks = state?.blocks ?? {}
5778

5879
for (const [blockId, block] of Object.entries(blocks)) {
59-
const b = block as any
60-
const blockType = b.type
80+
const blockType = block.type
6181

6282
// Check if block type is supported
6383
if (!SUPPORTED_BLOCK_TYPES.has(blockType)) {
6484
unsupportedBlocks.push({
6585
id: blockId,
66-
name: b.name || blockId,
86+
name: block.name ?? blockId,
6787
type: blockType,
6888
})
6989
}
7090

7191
// For agent blocks, check if the provider is supported
7292
if (blockType === 'agent') {
73-
const model = b.subBlocks?.model?.value || b.inputs?.model || ''
93+
const model = block.subBlocks?.model?.value ?? block.inputs?.model ?? ''
7494
const provider = detectProviderFromModel(model)
7595

7696
if (!SUPPORTED_PROVIDERS.has(provider)) {
7797
unsupportedProviders.push({
7898
id: blockId,
79-
name: b.name || blockId,
99+
name: block.name ?? blockId,
80100
model: model,
81101
provider: provider,
82102
})
@@ -139,8 +159,10 @@ function transpileJsToPython(code: string): string {
139159
code = code.replace(/(len\([^)]+\))\s*\+\s*(['"][^'"]*['"])/g, 'str($1) + $2')
140160

141161
// Transform property access (but not method calls)
162+
// Note: This handles simple bracket notation like arr[0].prop but not deeply nested
163+
// patterns like arr[obj["key"]].prop. For complex cases, use bracket notation in source.
142164
code = code.replace(
143-
/\b([a-zA-Z_][a-zA-Z0-9_]*(?:\[[^\]]+\])*)\.([a-zA-Z_][a-zA-Z0-9_]*)(?![a-zA-Z0-9_])(?!\s*\()/g,
165+
/\b([a-zA-Z_][a-zA-Z0-9_]*(?:\["[^"]*"\]|\['[^']*'\]|\[\d+\])*)\.([a-zA-Z_][a-zA-Z0-9_]*)(?![a-zA-Z0-9_])(?!\s*\()/g,
144166
'$1["$2"]'
145167
)
146168

@@ -197,11 +219,28 @@ function transpileJsToPython(code: string): string {
197219
return result.join('\n')
198220
}
199221

222+
// Type for export workflow state structure
223+
interface ExportWorkflowState {
224+
state?: {
225+
blocks?: Record<string, {
226+
type: string
227+
subBlocks?: {
228+
code?: { value?: string }
229+
language?: { value?: string }
230+
[key: string]: unknown
231+
}
232+
[key: string]: unknown
233+
}>
234+
[key: string]: unknown
235+
}
236+
[key: string]: unknown
237+
}
238+
200239
/**
201240
* Pre-transpile all JavaScript function blocks in a workflow state to Python.
202241
* Handles the ExportWorkflowState structure: {version, exportedAt, state: {blocks, ...}}
203242
*/
204-
function preTranspileWorkflow(exportState: any): any {
243+
function preTranspileWorkflow<T extends ExportWorkflowState>(exportState: T): T {
205244
// Handle ExportWorkflowState structure
206245
const blocks = exportState?.state?.blocks
207246
if (!blocks) return exportState
@@ -2341,10 +2380,16 @@ export async function GET(
23412380
{ headers: internalHeaders }
23422381
)
23432382

2344-
let workflowVariables: any[] = []
2383+
interface WorkflowVariable {
2384+
id: string
2385+
name: string
2386+
type: string
2387+
value: unknown
2388+
}
2389+
let workflowVariables: WorkflowVariable[] = []
23452390
if (variablesResponse.ok) {
2346-
const varsData = await variablesResponse.json()
2347-
workflowVariables = Object.values(varsData?.data || {}).map((v: any) => ({
2391+
const varsData = await variablesResponse.json() as { data?: Record<string, WorkflowVariable> }
2392+
workflowVariables = Object.values(varsData?.data ?? {}).map((v) => ({
23482393
id: v.id,
23492394
name: v.name,
23502395
type: v.type,

0 commit comments

Comments
 (0)