| 1 |
// Caches the in-flight Promise (not the resolved value) so concurrent |
| 2 |
// hover-prefetch + click consumers share one request. |
| 3 |
import apiFetch from '@wordpress/api-fetch'; |
| 4 |
import { addQueryArgs } from '@wordpress/url'; |
| 5 |
|
| 6 |
const cache = new Map(); |
| 7 |
|
| 8 |
// Resolve a source descriptor + block id into the get-block-code query args and |
| 9 |
// a collision-proof cache key. post and template-part sources number their |
| 10 |
// blocks in separate spaces (post #5 ≠ header #5), so the key carries the kind |
| 11 |
// and its discriminator. Returns null for sources QE loads through other |
| 12 |
// editors (product, wpforms, wp-navigation) or for incomplete input. |
| 13 |
const describe = (source, blockId) => { |
| 14 |
if (!source || !blockId) return null; |
| 15 |
if (source.kind === 'post' && source.id) { |
| 16 |
return { |
| 17 |
key: `post-${source.id}-${blockId}`, |
| 18 |
args: { postId: source.id, blockId }, |
| 19 |
}; |
| 20 |
} |
| 21 |
if (source.kind === 'template-part' && source.partSlug) { |
| 22 |
return { |
| 23 |
key: `part-${source.partSlug}-${blockId}`, |
| 24 |
args: { partSlug: source.partSlug, blockId }, |
| 25 |
}; |
| 26 |
} |
| 27 |
return null; |
| 28 |
}; |
| 29 |
|
| 30 |
export const prefetchBlockSource = (source, blockId) => { |
| 31 |
const desc = describe(source, blockId); |
| 32 |
if (!desc) return null; |
| 33 |
if (cache.has(desc.key)) return cache.get(desc.key); |
| 34 |
const promise = apiFetch({ |
| 35 |
path: addQueryArgs('/extendify/v1/agent/get-block-code', desc.args), |
| 36 |
}).catch((err) => { |
| 37 |
// Evict on failure so the next consumer (typically BlockTextEditor) |
| 38 |
// retries fresh and surfaces the error. |
| 39 |
cache.delete(desc.key); |
| 40 |
throw err; |
| 41 |
}); |
| 42 |
cache.set(desc.key, promise); |
| 43 |
return promise; |
| 44 |
}; |
| 45 |
|
| 46 |
// Same shape as prefetch but semantically "I need this now." |
| 47 |
export const getBlockSource = prefetchBlockSource; |
| 48 |
|
| 49 |
// Drop a stale entry after a save — splice rewrites the live DOM but the |
| 50 |
// cached source payload is still pre-save, so a re-edit would mount the |
| 51 |
// editor against the old markup. |
| 52 |
export const invalidateBlockSource = (source, blockId) => { |
| 53 |
const desc = describe(source, blockId); |
| 54 |
if (!desc) return; |
| 55 |
cache.delete(desc.key); |
| 56 |
}; |
| 57 |
|