UNPKG

@atlaskit/node-data-provider

Version:

Node data provider for @atlaskit/editor-core plugins and @atlaskit/renderer

179 lines (176 loc) 4.36 kB
import { findNodesToPrefetch } from './find-nodes-to-prefetch'; /** * Represents the SSR data for a single provider. * It's a map where each key is a unique node data key and the value is the prefetched data for that node. * * @example * { * 'node-id-1': { value: 'some data' }, * 'node-id-2': { value: 'other data' } * } */ /** * Represents the aggregated SSR data for all node data providers. * Each key is a provider's name, and the value contains the fetch status, duration, * and a map of node data keys to their prefetched data. * * @example * ``` * { * mentionProvider: { * success: true, * duration: 220, * data: { * 'mention-1': { id: '1', name: 'John Doe' } * } * }, * emojiProvider: { * success: true, * duration: 110, * data: { * 'emoji-123': { shortName: ':smile:', representation: '😊' } * } * } * } * ``` */ /** * Fetches data for nodes in the document that are supported by the given providers. * This function will traverse the document and call the `fetchData` method for each node that is supported by the providers. * * @example * ``` * const doc = JSON.parse('{"type": "doc", "content": [...] }'); * const providers = [ * { * provider: new EditorCardProvider(), * maxNodesToPrefetch: 10, * timeout: 500, * }, * { * provider: new EditorMentionsProvider(), * maxNodesToPrefetch: 50, * timeout: 500, * }, * ]; * * const data = await prefetchNodeDataProvidersData({ * providers, * doc, * timeout: 1_000, * maxNodesToVisit: 2_000 * }); * ``` * * @param props The properties for prefetching node data. * @returns Record of provider names to their respective SSR data, * success status, and duration of the fetch operation. */ export async function prefetchNodeDataProvidersData({ providers, doc, timeout, maxNodesToVisit = Infinity }) { if (timeout <= 0) { // If the global timeout is 0 or negative, skip fetching entirely. return providers.reduce((acc, { provider }) => { acc[provider.name] = { data: {}, success: false, duration: 0 }; return acc; }, {}); } const providersWithDefaults = providers.map(({ provider, maxNodesToPrefetch = Infinity, timeout: providerTimeout = Infinity }) => ({ provider, maxNodesToPrefetch, // Use the minimum of the global timeout and the provider-specific timeout timeout: Math.min(providerTimeout, timeout) })); const providerTimeouts = providersWithDefaults.reduce((acc, { provider, timeout }) => { acc[provider.name] = timeout; return acc; }, {}); const nodesWithProviders = findNodesToPrefetch(doc, providersWithDefaults, maxNodesToVisit).map(({ provider, nodes }) => ({ provider, nodes, timeout: providerTimeouts[provider.name] })); const promises = nodesWithProviders.map(async ({ nodes, provider, timeout }) => { if (timeout <= 0) { return { provider, success: false, duration: 0, nodes, data: [] }; } const start = performance.now(); function getDurationFromStart() { return Math.min(performance.now() - start, timeout); } try { const timeoutPromise = new Promise((_, reject) => { setTimeout(() => { reject(); }, timeout); }); const data = await Promise.race([provider.fetchNodesData(nodes), timeoutPromise]); return { provider, success: true, duration: getDurationFromStart(), nodes, data }; } catch { return { provider, success: false, duration: getDurationFromStart(), nodes, data: [] }; } }); const results = await Promise.all(promises); return results.reduce((acc, { provider, success, duration, nodes, data }) => { const ssrData = data.reduce((providerSsrData, nodeData, nodeIndex) => { const node = nodes[nodeIndex]; const nodeDataKey = provider.nodeDataKey(node); providerSsrData[nodeDataKey] = nodeData; return providerSsrData; }, {}); acc[provider.name] = { data: ssrData, success, duration }; return acc; }, {}); }