From 7c6463d7f7f3e1571b34585190a2239797deee35 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Thu, 13 Nov 2025 18:31:04 -0800 Subject: [PATCH 1/5] fix(onedrive): parse array values correctly --- apps/sim/blocks/blocks/onedrive.ts | 48 +++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/apps/sim/blocks/blocks/onedrive.ts b/apps/sim/blocks/blocks/onedrive.ts index 9751a05964a..c1cad2be284 100644 --- a/apps/sim/blocks/blocks/onedrive.ts +++ b/apps/sim/blocks/blocks/onedrive.ts @@ -2,10 +2,46 @@ import { MicrosoftOneDriveIcon } from '@/components/icons' import { createLogger } from '@/lib/logs/console/logger' import type { BlockConfig } from '@/blocks/types' import { AuthMode } from '@/blocks/types' -import type { OneDriveResponse } from '@/tools/onedrive/types' +import type { OneDriveResponse, OneDriveToolParams } from '@/tools/onedrive/types' const logger = createLogger('OneDriveBlock') +type ExcelValues = OneDriveToolParams['values'] + +/** + * Normalizes Excel values so that downstream tooling always receives an array. + */ +function normalizeExcelValues(values: unknown): ExcelValues | undefined { + if (values === null || values === undefined || values === '') { + return undefined + } + + if (Array.isArray(values)) { + return values as ExcelValues + } + + if (typeof values === 'string') { + const trimmed = values.trim() + if (!trimmed) { + return undefined + } + + try { + const parsed = JSON.parse(trimmed) + + if (!Array.isArray(parsed)) { + throw new Error('Excel values must be an array of rows or array of objects') + } + + return parsed as ExcelValues + } catch (_error) { + throw new Error('Invalid JSON format for values') + } + } + + throw new Error('Excel values must be an array of rows or array of objects') +} + export const OneDriveBlock: BlockConfig = { type: 'onedrive', name: 'OneDrive', @@ -351,17 +387,15 @@ export const OneDriveBlock: BlockConfig = { params: (params) => { const { credential, folderId, fileId, mimeType, values, downloadFileName, ...rest } = params - let parsedValues - try { - parsedValues = values ? JSON.parse(values as string) : undefined - } catch (error) { - throw new Error('Invalid JSON format for values') + let normalizedValues: ExcelValues | undefined + if (values !== undefined) { + normalizedValues = normalizeExcelValues(values) } return { credential, ...rest, - values: parsedValues, + values: normalizedValues, folderId: folderId || undefined, fileId: fileId || undefined, pageSize: rest.pageSize ? Number.parseInt(rest.pageSize as string, 10) : undefined, From 87d9c3f305777ceb5504b69be9458f810ef36708 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Thu, 13 Nov 2025 18:54:45 -0800 Subject: [PATCH 2/5] fix onedrive --- .../app/api/tools/onedrive/upload/route.ts | 16 ++++-- apps/sim/blocks/blocks/onedrive.ts | 43 ++-------------- apps/sim/tools/onedrive/utils.ts | 49 +++++++++++++++++++ 3 files changed, 66 insertions(+), 42 deletions(-) create mode 100644 apps/sim/tools/onedrive/utils.ts diff --git a/apps/sim/app/api/tools/onedrive/upload/route.ts b/apps/sim/app/api/tools/onedrive/upload/route.ts index f1276e3ad43..1f4a0f9f49a 100644 --- a/apps/sim/app/api/tools/onedrive/upload/route.ts +++ b/apps/sim/app/api/tools/onedrive/upload/route.ts @@ -6,6 +6,7 @@ import { createLogger } from '@/lib/logs/console/logger' import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' import { generateRequestId } from '@/lib/utils' +import { normalizeExcelValues } from '@/tools/onedrive/utils' export const dynamic = 'force-dynamic' @@ -13,6 +14,14 @@ const logger = createLogger('OneDriveUploadAPI') const MICROSOFT_GRAPH_BASE = 'https://graph.microsoft.com/v1.0' +const ExcelCellSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]) +const ExcelRowSchema = z.array(ExcelCellSchema) +const ExcelValuesSchema = z.union([ + z.string(), + z.array(ExcelRowSchema), + z.array(z.record(ExcelCellSchema)), +]) + const OneDriveUploadSchema = z.object({ accessToken: z.string().min(1, 'Access token is required'), fileName: z.string().min(1, 'File name is required'), @@ -20,7 +29,7 @@ const OneDriveUploadSchema = z.object({ folderId: z.string().optional().nullable(), mimeType: z.string().optional(), // Optional Excel write-after-create inputs - values: z.array(z.array(z.union([z.string(), z.number(), z.boolean(), z.null()]))).optional(), + values: ExcelValuesSchema.optional(), }) export async function POST(request: NextRequest) { @@ -46,6 +55,7 @@ export async function POST(request: NextRequest) { const body = await request.json() const validatedData = OneDriveUploadSchema.parse(body) + const excelValues = normalizeExcelValues(validatedData.values) let fileBuffer: Buffer let mimeType: string @@ -180,7 +190,7 @@ export async function POST(request: NextRequest) { // If this is an Excel creation and values were provided, write them using the Excel API let excelWriteResult: any | undefined const shouldWriteExcelContent = - isExcelCreation && Array.isArray(validatedData.values) && validatedData.values.length > 0 + isExcelCreation && Array.isArray(excelValues) && excelValues.length > 0 if (shouldWriteExcelContent) { try { @@ -232,7 +242,7 @@ export async function POST(request: NextRequest) { logger.warn(`[${requestId}] Error listing worksheets, using default Sheet1`, listError) } - let processedValues: any = validatedData.values || [] + let processedValues: any = excelValues || [] if ( Array.isArray(processedValues) && diff --git a/apps/sim/blocks/blocks/onedrive.ts b/apps/sim/blocks/blocks/onedrive.ts index c1cad2be284..34dcb3016b6 100644 --- a/apps/sim/blocks/blocks/onedrive.ts +++ b/apps/sim/blocks/blocks/onedrive.ts @@ -2,46 +2,11 @@ import { MicrosoftOneDriveIcon } from '@/components/icons' import { createLogger } from '@/lib/logs/console/logger' import type { BlockConfig } from '@/blocks/types' import { AuthMode } from '@/blocks/types' -import type { OneDriveResponse, OneDriveToolParams } from '@/tools/onedrive/types' +import type { OneDriveResponse } from '@/tools/onedrive/types' +import { normalizeExcelValuesForToolParams } from '@/tools/onedrive/utils' const logger = createLogger('OneDriveBlock') -type ExcelValues = OneDriveToolParams['values'] - -/** - * Normalizes Excel values so that downstream tooling always receives an array. - */ -function normalizeExcelValues(values: unknown): ExcelValues | undefined { - if (values === null || values === undefined || values === '') { - return undefined - } - - if (Array.isArray(values)) { - return values as ExcelValues - } - - if (typeof values === 'string') { - const trimmed = values.trim() - if (!trimmed) { - return undefined - } - - try { - const parsed = JSON.parse(trimmed) - - if (!Array.isArray(parsed)) { - throw new Error('Excel values must be an array of rows or array of objects') - } - - return parsed as ExcelValues - } catch (_error) { - throw new Error('Invalid JSON format for values') - } - } - - throw new Error('Excel values must be an array of rows or array of objects') -} - export const OneDriveBlock: BlockConfig = { type: 'onedrive', name: 'OneDrive', @@ -387,9 +352,9 @@ export const OneDriveBlock: BlockConfig = { params: (params) => { const { credential, folderId, fileId, mimeType, values, downloadFileName, ...rest } = params - let normalizedValues: ExcelValues | undefined + let normalizedValues: ReturnType if (values !== undefined) { - normalizedValues = normalizeExcelValues(values) + normalizedValues = normalizeExcelValuesForToolParams(values) } return { diff --git a/apps/sim/tools/onedrive/utils.ts b/apps/sim/tools/onedrive/utils.ts new file mode 100644 index 00000000000..e1b595f944b --- /dev/null +++ b/apps/sim/tools/onedrive/utils.ts @@ -0,0 +1,49 @@ +import type { OneDriveToolParams } from '@/tools/onedrive/types' + +export type ExcelCell = string | number | boolean | null +export type ExcelArrayValues = ExcelCell[][] +export type ExcelObjectValues = Array> +export type NormalizedExcelValues = ExcelArrayValues | ExcelObjectValues + +/** + * Ensures Excel values are always represented as arrays before hitting downstream tooling. + * Accepts JSON strings, array-of-arrays, or array-of-objects and normalizes them. + */ +export function normalizeExcelValues(values: unknown): NormalizedExcelValues | undefined { + if (values === null || values === undefined) { + return undefined + } + + if (typeof values === 'string') { + const trimmed = values.trim() + if (!trimmed) { + return undefined + } + + try { + const parsed = JSON.parse(trimmed) + if (!Array.isArray(parsed)) { + throw new Error('Excel values must be an array of rows or array of objects') + } + return parsed as NormalizedExcelValues + } catch (_error) { + throw new Error('Invalid JSON format for values') + } + } + + if (Array.isArray(values)) { + return values as NormalizedExcelValues + } + + throw new Error('Excel values must be an array of rows or array of objects') +} + +/** + * Convenience helper for contexts that expect the narrower ToolParams typing. + */ +export function normalizeExcelValuesForToolParams( + values: unknown +): OneDriveToolParams['values'] | undefined { + const normalized = normalizeExcelValues(values) + return normalized as OneDriveToolParams['values'] | undefined +} From bb4ec6fb792f96f3da3258f9a0f5fb1805eb5b76 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Thu, 13 Nov 2025 19:09:26 -0800 Subject: [PATCH 3/5] fix --- .../sub-block/components/code/code.tsx | 42 +++++++++++++++---- .../editor/components/sub-block/sub-block.tsx | 2 +- apps/sim/blocks/blocks/onedrive.ts | 15 +++++-- apps/sim/blocks/types.ts | 2 +- 4 files changed, 47 insertions(+), 14 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/code/code.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/code/code.tsx index 02092f9eb8a..8fec01358a4 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/code/code.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/code/code.tsx @@ -221,17 +221,26 @@ export function Code({ // Derived state const effectiveLanguage = (languageValue as 'javascript' | 'python' | 'json') || language + const trimmedCode = code.trim() + const containsReferencePlaceholders = + trimmedCode.includes('{{') || + trimmedCode.includes('}}') || + trimmedCode.includes('<') || + trimmedCode.includes('>') + + const shouldValidateJson = effectiveLanguage === 'json' && !containsReferencePlaceholders + const isValidJson = useMemo(() => { - if (subBlockId !== 'responseFormat' || !code.trim()) { + if (!shouldValidateJson || !trimmedCode) { return true } try { - JSON.parse(code) + JSON.parse(trimmedCode) return true } catch { return false } - }, [subBlockId, code]) + }, [shouldValidateJson, trimmedCode]) const gutterWidthPx = useMemo(() => { const lineCount = code.split('\n').length @@ -309,14 +318,29 @@ export function Code({ : storeValue // Effects: JSON validation + const lastValidationStatus = useRef(true) + useEffect(() => { - if (onValidationChange && subBlockId === 'responseFormat') { - const timeoutId = setTimeout(() => { - onValidationChange(isValidJson) - }, 150) - return () => clearTimeout(timeoutId) + if (!onValidationChange) return + + const nextStatus = shouldValidateJson ? isValidJson : true + if (lastValidationStatus.current === nextStatus) { + return } - }, [isValidJson, onValidationChange, subBlockId]) + + lastValidationStatus.current = nextStatus + + if (!shouldValidateJson) { + onValidationChange(nextStatus) + return + } + + const timeoutId = setTimeout(() => { + onValidationChange(nextStatus) + }, 150) + + return () => clearTimeout(timeoutId) + }, [isValidJson, onValidationChange, shouldValidateJson]) // Effects: AI stream handlers setup useEffect(() => { diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/sub-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/sub-block.tsx index 8328eb7be9d..03c03b9335e 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/sub-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/sub-block.tsx @@ -190,7 +190,7 @@ const renderLabel = (
{config.title} {required && *} - {config.id === 'responseFormat' && ( + {config.type === 'code' && config.language === 'json' && ( = { { id: 'values', title: 'Values', - type: 'long-input', + type: 'code', + language: 'json', + generationType: 'json-object', placeholder: - 'Enter values as JSON array of arrays (e.g., [["A1","B1"],["A2","B2"]]) or an array of objects', + 'Enter a JSON array of rows (e.g., [["A1","B1"],["A2","B2"]]) or use a reference like {{block_1.rows}}', condition: { field: 'operation', value: 'create_file', @@ -90,6 +92,13 @@ export const OneDriveBlock: BlockConfig = { value: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', }, }, + wandConfig: { + enabled: true, + prompt: + 'Generate a JSON array of arrays that can be written directly into an Excel worksheet.', + placeholder: 'Describe the table you want to generate...', + generationType: 'json-object', + }, required: false, }, // File upload (basic mode) @@ -379,7 +388,7 @@ export const OneDriveBlock: BlockConfig = { fileReference: { type: 'json', description: 'File reference from previous block' }, content: { type: 'string', description: 'Text content to upload' }, mimeType: { type: 'string', description: 'MIME type of file to create' }, - values: { type: 'string', description: 'Cell values for new Excel as JSON' }, + values: { type: 'json', description: 'Cell values for new Excel as JSON' }, fileId: { type: 'string', description: 'File ID to download' }, downloadFileName: { type: 'string', description: 'File name override for download' }, folderId: { type: 'string', description: 'Folder ID' }, diff --git a/apps/sim/blocks/types.ts b/apps/sim/blocks/types.ts index 568c32100a7..ac2c65bad61 100644 --- a/apps/sim/blocks/types.ts +++ b/apps/sim/blocks/types.ts @@ -210,7 +210,7 @@ export interface SubBlockConfig { } }) // Props specific to 'code' sub-block type - language?: 'javascript' | 'json' + language?: 'javascript' | 'json' | 'python' generationType?: GenerationType collapsible?: boolean // Whether the code block can be collapsed defaultCollapsed?: boolean // Whether the code block is collapsed by default From 03a962fdc238e621e6978066e5b9ed45aa6a0950 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Thu, 13 Nov 2025 19:12:53 -0800 Subject: [PATCH 4/5] fix onedrive input parsing by reusing code subblock --- apps/sim/blocks/blocks/onedrive.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/sim/blocks/blocks/onedrive.ts b/apps/sim/blocks/blocks/onedrive.ts index 02e8a57b2b5..e4698a9e52a 100644 --- a/apps/sim/blocks/blocks/onedrive.ts +++ b/apps/sim/blocks/blocks/onedrive.ts @@ -82,8 +82,7 @@ export const OneDriveBlock: BlockConfig = { type: 'code', language: 'json', generationType: 'json-object', - placeholder: - 'Enter a JSON array of rows (e.g., [["A1","B1"],["A2","B2"]]) or use a reference like {{block_1.rows}}', + placeholder: 'Enter a JSON array of rows (e.g., [["A1","B1"],["A2","B2"]])', condition: { field: 'operation', value: 'create_file', From f9cd33054a3fac6220a77a8ef76f56c09db600b9 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Thu, 13 Nov 2025 19:18:49 -0800 Subject: [PATCH 5/5] fix type --- apps/sim/tools/onedrive/types.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/sim/tools/onedrive/types.ts b/apps/sim/tools/onedrive/types.ts index cd20b6e2f07..6ceb005c5e3 100644 --- a/apps/sim/tools/onedrive/types.ts +++ b/apps/sim/tools/onedrive/types.ts @@ -99,7 +99,9 @@ export interface OneDriveToolParams { pageToken?: string exportMimeType?: string // Optional Excel write parameters (used when creating an .xlsx without file content) - values?: (string | number | boolean | null)[][] + values?: + | (string | number | boolean | null)[][] + | Array> } export type OneDriveResponse =