UNPKG

@shopify/cli-kit

Version:

A set of utilities, interfaces, and models that are common across all the platform features

123 lines 5.76 kB
import { isUnitTest } from './context/local.js'; import { performance } from 'node:perf_hooks'; /** * Get the error handling strategy for metadata. * * @returns 'mute-and-report' in production, 'bubble' in tests. */ function getMetadataErrorHandlingStrategy() { if (isUnitTest()) { return 'bubble'; } return 'mute-and-report'; } /** * Creates a container for metadata collected at runtime. * The container provides async-safe functions for extracting the gathered metadata, and for setting it. * * @param defaultPublicMetadata - Optional, default data for the container. * @returns A container for the metadata. */ export function createRuntimeMetadataContainer(defaultPublicMetadata = {}) { const raw = { sensitive: {}, public: { ...defaultPublicMetadata, }, }; const addPublic = (data) => { Object.assign(raw.public, data); }; const addSensitive = (data) => { Object.assign(raw.sensitive, data); }; const addMetadata = async (addFn, getFn, onError) => { const errorHandling = onError === 'auto' ? getMetadataErrorHandlingStrategy() : onError; const getAndSet = async () => { const data = await getFn(); addFn(data); }; if (errorHandling === 'bubble') { await getAndSet(); } else { try { await getAndSet(); // eslint-disable-next-line no-catch-all/no-catch-all, @typescript-eslint/no-explicit-any } catch (error) { // This is very prone to becoming a circular dependency, so we import it dynamically const { sendErrorToBugsnag } = await import('./error-handler.js'); await sendErrorToBugsnag(error, 'unexpected_error'); } } }; // See `runWithTimer` below. const durationStack = []; return { getAllPublicMetadata: () => { return { ...raw.public }; }, getAllSensitiveMetadata: () => { return { ...raw.sensitive }; }, addPublicMetadata: async (getData, onError = 'auto') => { return addMetadata(addPublic, getData, onError); }, addSensitiveMetadata: async (getData, onError = 'auto') => { return addMetadata(addSensitive, getData, onError); }, runWithTimer: (field) => { return async (fn) => { /** * For nested timers, we subtract the inner timer's duration from the outer timer's. We use a stack to track the * cumulative durations of nested timers. On starting a timer, we push a zero onto the stack to initialize the total * duration for subsequent nested timers. Before logging, we pop the stack to get the total nested timers' duration. * We subtract this from the current timer's actual duration to get its measurable duration. We then add the current * timer's actual duration to the stack's top, allowing any parent timer to deduct it from its own duration. */ // Initialise the running total duration for all nested timers durationStack.push(0); // Do the work, and time it const start = performance.now(); try { const result = await fn(); return result; } finally { let end = performance.now(); // For very short durations, the end time can be before the start time(!) - we flatten this out to zero. end = Math.max(start, end); // The top of the stack is the total time for all nested timers const wallClockDuration = Math.max(end - start, 0); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const childDurations = durationStack.pop(); const duration = Math.max(wallClockDuration - childDurations, 0); // If this is the topmost timer, the stack will be empty. if (durationStack.length > 0) { durationStack[durationStack.length - 1] = (durationStack[durationStack.length - 1] ?? 0) + wallClockDuration; } // Log it -- we include it in the metadata, but also log via the standard performance API. The TS types for this library are not quite right, so we have to cast to `any` here. performance.measure(`${field}#measurable`, { start, duration, // eslint-disable-next-line @typescript-eslint/no-explicit-any }); performance.measure(`${field}#wall`, { start, end, // eslint-disable-next-line @typescript-eslint/no-explicit-any }); // There might not be a value set, yet let currentValue = (raw.public[field] || 0); currentValue += duration; // TS is not quite smart enough to realise that raw.public[field] must be a numeric type raw.public[field] = currentValue; } }; }, }; } const coreData = createRuntimeMetadataContainer({ cmd_all_timing_network_ms: 0, cmd_all_timing_prompts_ms: 0 }); export const { getAllPublicMetadata, getAllSensitiveMetadata, addPublicMetadata, addSensitiveMetadata, runWithTimer } = coreData; //# sourceMappingURL=metadata.js.map