UNPKG

stagnant

Version:

Measure your slow code, make it _fast_.

238 lines (196 loc) 7.42 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.stagnant = factory()); }(this, (function () { 'use strict'; function defaultConfig(){ function onevent(){} function onerror(){} function onsuccess(){} function onflush(){} // eslint-disable-next-line no-undef const ourConsole = { log(){}, error(){}, warn(){} }; function generateId(){ return Math.random().toString(15).slice(2,8) } return { onevent, onerror, onsuccess, onflush, console: ourConsole, generateId } } function Main(config={}){ config = { ...defaultConfig(), ...config }; const { generateId } = config; async function dispatchEvent(event){ await config.onevent(event); if( event.error ) { await config.onerror(event); } else { await config.onsuccess(event); } } function Event({ parentId, id, startTime, endTime, name, data, error }){ return { parentId, id, startTime, endTime, name, data, error } } function RootEvent(){ const event = Event({ parentId: null ,id: generateId() ,startTime: Date.now() ,endTime: Date.now() ,error: null ,data: {} }); event.flush = async function flush(){ delete event.flush; event.endTime = Date.now(); await dispatchEvent(event); await config.onflush(event); return event }; return event } function setupEvent({ parentEvent, name, data, sync }){ const event = Event({ parentId: parentEvent.id ,id: generateId() ,name ,startTime: null ,endTime: null ,data: { ...parentEvent.data || {}, ...data } ,error: null ,sync: parentEvent.sync || sync }); const childP = Instance(event); return { childP, event } } function Instance(parentEvent){ function handler(...args){ const callbackIndex = args.findIndex( x => typeof x == 'function' ); let cb = args[callbackIndex]; let rest = callbackIndex > 0 ? args.slice(0,callbackIndex) : args; let [name, data] = rest; if (typeof name != 'string') { data = name; if( cb ) { name = data.name || cb.toString().replace( /\s/g, '' ).replace( /(.)*=>/, '' ); } } if ( data == null ) data = {}; return { name, data, callback: cb } } function handlerData({ data }){ parentEvent.data = { ...parentEvent.data, ...data }; return null } async function handlerAsync({ event, childP, callback }){ if ( parentEvent.sync ) { throw new Error('Cannot use an async trace within a synchronous trace') } try { event.startTime = Date.now(); const out = await callback(childP); event.endTime = Date.now(); return out } catch (e) { event.endTime = Date.now(); event.error = e; throw e } finally { dispatchEvent(event) .catch( e => config.console.error('Failed to dispatch event', e)); } } function handlerSync({ callback, name, event, childP }){ try { event.startTime = Date.now(); const out = callback(childP); event.endTime = Date.now(); if( out != null && 'then' in out ) { config.console.warn(name, 'A call to trace.sync was made but the response was async. This is likely a mistake and should be corrected.'); } return out } catch (e) { event.endTime = Date.now(); event.error = e; throw e } finally { dispatchEvent(event) .catch( e => config.console.error('Failed to dispatch event', e)); } } function routerOptions({ sync }, ...args){ const { data, callback, name } = handler(...args); const {event,childP} = callback ? setupEvent({ parentEvent, name, data, sync }) : {}; if( callback && sync ) { return handlerSync({ data, callback, childP, name, event }) } else if ( callback && !sync ) { return handlerAsync({ data, callback, childP, name, event }) } else { return handlerData({ data }) } } async function routerAsync(...args){ const out = await routerOptions({ sync: false }, ...args); return out } function routerSync(...args){ const out = routerOptions({ sync: true }, ...args); return out } routerAsync.sync = routerSync; return routerAsync } let rootEvent = RootEvent(); let handlerInstance = Instance(rootEvent); handlerInstance.flush = rootEvent.flush; handlerInstance.config = config; return handlerInstance } /** * Safely invoke a callback even if trace is null. * * useful when there are multiple entry points into a function and some are not * passing in a trace. * * @param {*} trace * @param {...any} args * @returns */ function call(trace, ...args){ const cb = args.find( x => typeof x == 'function' ); if ( trace ) { return trace( ...args ) } else if ( cb ) { return cb( (...args) => call(null, ...args) ) } return null } Main.call = call; /** * Create a no-op trace if provided trace is null * * Much like stagnant.call, useful when there are multiple entry points into a function and some are not * passing in a trace. * * @param {*} trace * @param {...any} args * @returns */ function ensure(trace){ if( trace ) { return trace } else { return (...args) => call(null, ...args) } } Main.ensure = ensure; return Main; }))); //# sourceMappingURL=stagnant.browser.js.map