Skip to content

Commit cb3a166

Browse files
committed
fix(docs): stop a comment hijacking the id scan, unhang a Firecrawl reference
The depth-1 walk copied from the raw source at indices where the blanked copy was at depth 1, so 'id:' inside a string value or a comment landed in the scanned text and won the first match. With the keep-bias that now means a phantom id can retain a param the block never exposes. Matching runs on the blanked text and reads the literal back through a source-index map. No block in the repo trips this today -- verified across all 305 -- so this is a latent fix. Removing the unsettable scrapeOptions row left five Firecrawl Search output descriptions referencing a name that no longer appears on the page. They now describe the response condition instead. Pointing them at 'formats' was not an option: that subBlock is conditioned on scrape/parse/batch_scrape and the search tool declares no such param, so it would have swapped one dangling reference for another.
1 parent 0a53bd9 commit cb3a166

4 files changed

Lines changed: 54 additions & 13 deletions

File tree

apps/docs/content/docs/en/integrations/firecrawl.mdx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,11 @@ Search for information on the web using Firecrawl
167167
|`title` | string | Search result title from search engine |
168168
|`description` | string | Search result description/snippet from search engine |
169169
|`url` | string | URL of the search result |
170-
|`markdown` | string | Page content in markdown \(when scrapeOptions.formats includes "markdown"\) |
171-
|`html` | string | Processed HTML content \(when scrapeOptions.formats includes "html"\) |
172-
|`rawHtml` | string | Unprocessed raw HTML \(when scrapeOptions.formats includes "rawHtml"\) |
173-
|`links` | array | Links found on the page \(when scrapeOptions.formats includes "links"\) |
174-
|`screenshot` | string | Screenshot URL \(expires after 24 hours, when scrapeOptions.formats includes "screenshot"\) |
170+
|`markdown` | string | Page content in markdown, present only when the search result includes scraped page content |
171+
|`html` | string | Processed HTML content, present only when the search result includes scraped page content |
172+
|`rawHtml` | string | Unprocessed raw HTML, present only when the search result includes scraped page content |
173+
|`links` | array | Links found on the page, present only when the search result includes scraped page content |
174+
|`screenshot` | string | Screenshot URL \(expires after 24 hours\), present only when the search result includes a page screenshot |
175175
|`metadata` | object | Metadata about the search result page |
176176
|`title` | string | Page title |
177177
|`description` | string | Page meta description |

apps/sim/tools/firecrawl/types.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,29 +135,33 @@ export const SEARCH_RESULT_OUTPUT_PROPERTIES = {
135135
url: { type: 'string', description: 'URL of the search result' },
136136
markdown: {
137137
type: 'string',
138-
description: 'Page content in markdown (when scrapeOptions.formats includes "markdown")',
138+
description:
139+
'Page content in markdown, present only when the search result includes scraped page content',
139140
optional: true,
140141
},
141142
html: {
142143
type: 'string',
143-
description: 'Processed HTML content (when scrapeOptions.formats includes "html")',
144+
description:
145+
'Processed HTML content, present only when the search result includes scraped page content',
144146
optional: true,
145147
},
146148
rawHtml: {
147149
type: 'string',
148-
description: 'Unprocessed raw HTML (when scrapeOptions.formats includes "rawHtml")',
150+
description:
151+
'Unprocessed raw HTML, present only when the search result includes scraped page content',
149152
optional: true,
150153
},
151154
links: {
152155
type: 'array',
153-
description: 'Links found on the page (when scrapeOptions.formats includes "links")',
156+
description:
157+
'Links found on the page, present only when the search result includes scraped page content',
154158
optional: true,
155159
items: { type: 'string', description: 'URL found on the page' },
156160
},
157161
screenshot: {
158162
type: 'string',
159163
description:
160-
'Screenshot URL (expires after 24 hours, when scrapeOptions.formats includes "screenshot")',
164+
'Screenshot URL (expires after 24 hours), present only when the search result includes a page screenshot',
161165
optional: true,
162166
},
163167
metadata: SEARCH_METADATA_OUTPUT,

scripts/generate-docs.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,24 @@ describe('subBlock param extraction', () => {
154154
}
155155
})
156156

157+
it('ignores an id inside a comment or string literal at the top level of a subBlock', () => {
158+
expect(
159+
extractUserSettableParamIds(`subBlocks: [\n { // id: 'ghost',\n id: 'real' },\n],`)
160+
).toEqual(['real'])
161+
162+
expect(
163+
extractUserSettableParamIds(
164+
`subBlocks: [\n { placeholder: "id: 'ghost'",\n id: 'real' },\n],`
165+
)
166+
).toEqual(['real'])
167+
168+
expect(
169+
extractUserSettableParamIds(
170+
`subBlocks: [\n { placeholder: "canonicalParamId: 'ghost'",\n id: 'real',\n canonicalParamId: 'canonical' },\n],`
171+
)
172+
).toEqual(['real', 'canonical'])
173+
})
174+
157175
it('throws when the subBlocks array holds literal objects but yields no ids', () => {
158176
expect(() =>
159177
extractUserSettableParamIds(`subBlocks: [\n { title: 'No id here' },\n],`)

scripts/generate-docs.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,7 @@ export function extractUserSettableParamIds(blockContent: string, blockName = 'b
702702

703703
let depth = 0
704704
let topLevel = ''
705+
const sourceIndices: number[] = []
705706
for (let k = i; k < objectEnd; k++) {
706707
const char = scannable[k]
707708
if (char === '{' || char === '[') {
@@ -712,13 +713,31 @@ export function extractUserSettableParamIds(blockContent: string, blockName = 'b
712713
depth--
713714
continue
714715
}
715-
if (depth === 1) topLevel += blockContent[k]
716+
if (depth === 1) {
717+
topLevel += char
718+
sourceIndices.push(k)
719+
}
720+
}
721+
722+
/**
723+
* Matching runs on the blanked characters, so an `id:` sitting inside a string value or a
724+
* `//` comment cannot be mistaken for the subBlock's own id. Blanking keeps a string's
725+
* quotes and its length, so the matched literal's value is read back character by character
726+
* from the original content at the indices the blanked copy matched at.
727+
*/
728+
const readLiteral = (match: RegExpExecArray): string => {
729+
const valueStart = match.index + match[0].length - 1 - match[1].length
730+
let value = ''
731+
for (let offset = 0; offset < match[1].length; offset++) {
732+
value += blockContent[sourceIndices[valueStart + offset]]
733+
}
734+
return value
716735
}
717736

718737
const idMatch = /\bid\s*:\s*['"]([^'"]+)['"]/.exec(topLevel)
719-
if (idMatch) ids.add(idMatch[1])
738+
if (idMatch) ids.add(readLiteral(idMatch))
720739
const canonicalMatch = /\bcanonicalParamId\s*:\s*['"]([^'"]+)['"]/.exec(topLevel)
721-
if (canonicalMatch) ids.add(canonicalMatch[1])
740+
if (canonicalMatch) ids.add(readLiteral(canonicalMatch))
722741

723742
i = objectEnd
724743
}

0 commit comments

Comments
 (0)