Skip to content

Commit ff9a404

Browse files
committed
fix(convex): separate List Documents page cursor from deltas cursor and surface HTTP errors in transforms
1 parent df365cc commit ff9a404

6 files changed

Lines changed: 77 additions & 13 deletions

File tree

apps/sim/blocks/blocks/convex.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ Return ONLY the JSON object - no explanations, no markdown, no extra text.`,
105105
condition: { field: 'operation', value: 'list_documents' },
106106
},
107107
{
108-
id: 'cursor',
108+
id: 'pageCursor',
109109
title: 'Cursor',
110110
type: 'short-input',
111111
mode: 'advanced',
@@ -144,6 +144,15 @@ Return ONLY the JSON object - no explanations, no markdown, no extra text.`,
144144
throw new Error(`Invalid Convex operation: ${params.operation}`)
145145
}
146146
},
147+
params: (params) => {
148+
const { pageCursor, ...rest } = params
149+
150+
if (params.operation === 'list_documents') {
151+
rest.cursor = pageCursor
152+
}
153+
154+
return rest
155+
},
147156
},
148157
},
149158
inputs: {
@@ -153,8 +162,9 @@ Return ONLY the JSON object - no explanations, no markdown, no extra text.`,
153162
functionPath: { type: 'string', description: 'Function path (e.g., messages:list)' },
154163
args: { type: 'json', description: 'Named arguments for the function' },
155164
tableName: { type: 'string', description: 'Table to read from (empty for all tables)' },
156-
snapshot: { type: 'string', description: 'Snapshot timestamp for pagination' },
157-
cursor: { type: 'string', description: 'Pagination cursor' },
165+
snapshot: { type: 'string', description: 'Snapshot timestamp for List Documents pagination' },
166+
cursor: { type: 'string', description: 'Timestamp cursor for Document Deltas' },
167+
pageCursor: { type: 'string', description: 'Pagination cursor for List Documents' },
158168
},
159169
outputs: {
160170
value: {

apps/sim/tools/convex/document_deltas.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
import type { ConvexDocumentDeltasParams, ConvexDocumentDeltasResponse } from '@/tools/convex/types'
2-
import { convexApiUrl, convexAuthHeaders } from '@/tools/convex/utils'
1+
import type {
2+
ConvexDocumentDeltasApiResponse,
3+
ConvexDocumentDeltasParams,
4+
ConvexDocumentDeltasResponse,
5+
} from '@/tools/convex/types'
6+
import { convexApiUrl, convexAuthHeaders, parseConvexResponse } from '@/tools/convex/utils'
37
import type { ToolConfig } from '@/tools/types'
48

59
export const documentDeltasTool: ToolConfig<
@@ -57,7 +61,7 @@ export const documentDeltasTool: ToolConfig<
5761
},
5862

5963
transformResponse: async (response: Response) => {
60-
const data = await response.json()
64+
const data = (await parseConvexResponse(response)) as ConvexDocumentDeltasApiResponse
6165

6266
return {
6367
success: true,

apps/sim/tools/convex/list_documents.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
import type { ConvexListDocumentsParams, ConvexListDocumentsResponse } from '@/tools/convex/types'
2-
import { convexApiUrl, convexAuthHeaders } from '@/tools/convex/utils'
1+
import type {
2+
ConvexListDocumentsParams,
3+
ConvexListDocumentsResponse,
4+
ConvexListSnapshotApiResponse,
5+
} from '@/tools/convex/types'
6+
import { convexApiUrl, convexAuthHeaders, parseConvexResponse } from '@/tools/convex/utils'
37
import type { ToolConfig } from '@/tools/types'
48

59
export const listDocumentsTool: ToolConfig<ConvexListDocumentsParams, ConvexListDocumentsResponse> =
@@ -59,7 +63,7 @@ export const listDocumentsTool: ToolConfig<ConvexListDocumentsParams, ConvexList
5963
},
6064

6165
transformResponse: async (response: Response) => {
62-
const data = await response.json()
66+
const data = (await parseConvexResponse(response)) as ConvexListSnapshotApiResponse
6367

6468
return {
6569
success: true,

apps/sim/tools/convex/list_tables.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ConvexListTablesParams, ConvexListTablesResponse } from '@/tools/convex/types'
2-
import { convexApiUrl, convexAuthHeaders } from '@/tools/convex/utils'
2+
import { convexApiUrl, convexAuthHeaders, parseConvexResponse } from '@/tools/convex/utils'
33
import type { ToolConfig } from '@/tools/types'
44

55
export const listTablesTool: ToolConfig<ConvexListTablesParams, ConvexListTablesResponse> = {
@@ -30,7 +30,7 @@ export const listTablesTool: ToolConfig<ConvexListTablesParams, ConvexListTables
3030
},
3131

3232
transformResponse: async (response: Response) => {
33-
const data = await response.json()
33+
const data = await parseConvexResponse(response)
3434
const schemas =
3535
data !== null && typeof data === 'object' && !Array.isArray(data)
3636
? (data as Record<string, unknown>)

apps/sim/tools/convex/types.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,34 @@ export interface ConvexDocumentDeltasResponse extends ToolResponse {
6060
}
6161
}
6262

63+
/**
64+
* Raw wire shape of Convex function-call responses (`/api/query`, `/api/mutation`,
65+
* `/api/action`, `/api/run/{identifier}`).
66+
* @see https://docs.convex.dev/http-api/#post-apiquery-apimutation-apiaction
67+
*/
68+
export interface ConvexFunctionCallApiResponse {
69+
status?: 'success' | 'error'
70+
value?: unknown
71+
logLines?: string[]
72+
errorMessage?: string
73+
errorData?: unknown
74+
}
75+
76+
/** Raw wire shape of `/api/list_snapshot` responses. */
77+
export interface ConvexListSnapshotApiResponse {
78+
values?: unknown[]
79+
hasMore?: boolean
80+
snapshot?: number | string | null
81+
cursor?: number | string | null
82+
}
83+
84+
/** Raw wire shape of `/api/document_deltas` responses. */
85+
export interface ConvexDocumentDeltasApiResponse {
86+
values?: unknown[]
87+
hasMore?: boolean
88+
cursor?: number | string | null
89+
}
90+
6391
export interface ConvexResponse extends ToolResponse {
6492
output: {
6593
value?: unknown

apps/sim/tools/convex/utils.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import type { ConvexFunctionCallResponse } from '@/tools/convex/types'
1+
import { truncate } from '@sim/utils/string'
2+
import type {
3+
ConvexFunctionCallApiResponse,
4+
ConvexFunctionCallResponse,
5+
} from '@/tools/convex/types'
26

37
/**
48
* Builds a Convex deployment API URL from the user-provided deployment URL.
@@ -47,6 +51,20 @@ export function parseFunctionArgs(
4751
return args
4852
}
4953

54+
/**
55+
* Parses a Convex API response body, surfacing non-OK HTTP statuses (e.g. 401
56+
* from an invalid deploy key) as descriptive errors instead of empty results.
57+
*/
58+
export async function parseConvexResponse(response: Response): Promise<unknown> {
59+
if (!response.ok) {
60+
const text = await response.text().catch(() => '')
61+
throw new Error(
62+
`Convex request failed (HTTP ${response.status})${text ? `: ${truncate(text.trim(), 300)}` : ''}`
63+
)
64+
}
65+
return response.json()
66+
}
67+
5068
/**
5169
* Transforms a Convex function-call response. Convex returns HTTP 200 with an
5270
* in-band `status: "error"` payload when the function itself fails, so errors
@@ -57,7 +75,7 @@ export async function transformFunctionCallResponse(
5775
response: Response,
5876
functionType: 'query' | 'mutation' | 'action' | 'function'
5977
): Promise<ConvexFunctionCallResponse> {
60-
const data = await response.json()
78+
const data = (await parseConvexResponse(response)) as ConvexFunctionCallApiResponse
6179

6280
if (data.status === 'error') {
6381
const details =

0 commit comments

Comments
 (0)