Skip to content

Commit 10bb0e7

Browse files
committed
fix(files): keep an embed id spelled as the document spells it
Decoding the id let a percent-encoded embed resolve and bundle its asset while the rewrite, which searches the document for that id, found nothing — the zip kept an API URL that renders as a broken image offline. Keys stay decoded; they are matched against stored keys, not against document text.
1 parent ed78cda commit 10bb0e7

2 files changed

Lines changed: 13 additions & 6 deletions

File tree

apps/sim/lib/uploads/utils/embedded-image-ref.test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@ describe('extractEmbeddedFileRef', () => {
2121
expect(extractEmbeddedFileRef('/workspace/W1/files/wf_abc')).toEqual({ fileId: 'wf_abc' })
2222
})
2323

24-
it('percent-decodes the id, like it already does the key', () => {
25-
expect(extractEmbeddedFileRef('/api/files/view/wf%5Fabc')).toEqual({ fileId: 'wf_abc' })
26-
expect(extractEmbeddedFileRef('/workspace/W1/files/wf%5Fabc')).toEqual({ fileId: 'wf_abc' })
24+
/**
25+
* The export bundler rewrites an embed by searching the document for the id it was handed, so a
26+
* decoded id would bundle the asset and leave the markdown pointing at the API URL.
27+
*/
28+
it('returns the id as spelled in the src, so the export can find it again', () => {
29+
expect(extractEmbeddedFileRef('/api/files/view/wf%5Fabc')).toEqual({ fileId: 'wf%5Fabc' })
30+
expect(extractEmbeddedFileRef('/workspace/W1/files/wf%5Fabc')).toEqual({ fileId: 'wf%5Fabc' })
2731
})
2832

2933
it('returns null for external, data, and non-workspace serve urls', () => {

apps/sim/lib/uploads/utils/embedded-image-ref.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ export type EmbeddedFileRef = { key: string } | { fileId: string } | null
1818
* and the in-app path `/workspace/<wsId>/files/<id>`. Returns null for absolute, `data:`, or non-workspace
1919
* URLs (e.g. public `profile-pictures/` assets), which render as-is.
2020
*
21-
* Path segments are percent-decoded, so callers always receive the real key or id.
21+
* A key is percent-decoded — it is matched against stored keys. An id is returned exactly as it is
22+
* spelled in the `src`, because the export bundler rewrites embeds by searching the document for
23+
* that spelling; handing it a decoded id it cannot find would bundle an asset and leave the markdown
24+
* pointing at the API URL, which renders as a broken image offline.
2225
*/
2326
export function extractEmbeddedFileRef(src: string): EmbeddedFileRef {
2427
try {
@@ -36,10 +39,10 @@ export function extractEmbeddedFileRef(src: string): EmbeddedFileRef {
3639
return key.startsWith('workspace/') ? { key } : null
3740
}
3841
if (segs[1] === 'api' && segs[2] === 'files' && segs[3] === 'view' && segs[4]) {
39-
return { fileId: decodeURIComponent(segs[4]) }
42+
return { fileId: segs[4] }
4043
}
4144
if (segs[1] === 'workspace' && segs[3] === 'files' && segs[4]) {
42-
return { fileId: decodeURIComponent(segs[4]) }
45+
return { fileId: segs[4] }
4346
}
4447
return null
4548
} catch {

0 commit comments

Comments
 (0)