UNPKG

7.42 kBJavaScriptView Raw
1(function (global, factory) {
2 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3 typeof define === 'function' && define.amd ? define(factory) :
4 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.stagnant = factory());
5}(this, (function () { 'use strict';
6
7 function defaultConfig(){
8
9 function onevent(){}
10 function onerror(){}
11 function onsuccess(){}
12 function onflush(){}
13
14 // eslint-disable-next-line no-undef
15
16 const ourConsole = {
17 log(){},
18 error(){},
19 warn(){}
20 };
21
22 function generateId(){
23 return Math.random().toString(15).slice(2,8)
24 }
25
26 return {
27 onevent, onerror, onsuccess, onflush, console: ourConsole, generateId
28 }
29 }
30
31 function Main(config={}){
32
33 config = { ...defaultConfig(), ...config };
34
35 const { generateId } = config;
36
37 async function dispatchEvent(event){
38 await config.onevent(event);
39 if( event.error ) {
40 await config.onerror(event);
41 } else {
42 await config.onsuccess(event);
43 }
44 }
45
46 function Event({
47 parentId, id, startTime, endTime, name, data, error
48 }){
49 return { parentId, id, startTime, endTime, name, data, error }
50 }
51
52 function RootEvent(){
53 const event = Event({
54 parentId: null
55 ,id: generateId()
56 ,startTime: Date.now()
57 ,endTime: Date.now()
58 ,error: null
59 ,data: {}
60 });
61
62 event.flush = async function flush(){
63 delete event.flush;
64 event.endTime = Date.now();
65 await dispatchEvent(event);
66 await config.onflush(event);
67 return event
68 };
69
70 return event
71 }
72
73 function setupEvent({ parentEvent, name, data, sync }){
74 const event = Event({
75 parentId: parentEvent.id
76 ,id: generateId()
77 ,name
78 ,startTime: null
79 ,endTime: null
80 ,data: { ...parentEvent.data || {}, ...data }
81 ,error: null
82 ,sync: parentEvent.sync || sync
83 });
84 const childP = Instance(event);
85 return { childP, event }
86 }
87
88
89 function Instance(parentEvent){
90
91 function handler(...args){
92 const callbackIndex = args.findIndex( x => typeof x == 'function' );
93 let cb = args[callbackIndex];
94 let rest = callbackIndex > 0 ? args.slice(0,callbackIndex) : args;
95
96 let [name, data] = rest;
97
98 if (typeof name != 'string') {
99 data = name;
100 if( cb ) {
101 name = data.name || cb.toString().replace( /\s/g, '' ).replace( /(.)*=>/, '' );
102 }
103 }
104
105 if ( data == null ) data = {};
106
107 return { name, data, callback: cb }
108 }
109
110 function handlerData({ data }){
111 parentEvent.data = { ...parentEvent.data, ...data };
112 return null
113 }
114
115 async function handlerAsync({ event, childP, callback }){
116 if ( parentEvent.sync ) {
117 throw new Error('Cannot use an async trace within a synchronous trace')
118 }
119
120 try {
121 event.startTime = Date.now();
122 const out = await callback(childP);
123 event.endTime = Date.now();
124 return out
125 } catch (e) {
126 event.endTime = Date.now();
127 event.error = e;
128 throw e
129 } finally {
130 dispatchEvent(event)
131 .catch( e => config.console.error('Failed to dispatch event', e));
132 }
133
134 }
135
136 function handlerSync({ callback, name, event, childP }){
137 try {
138 event.startTime = Date.now();
139 const out = callback(childP);
140 event.endTime = Date.now();
141 if( out != null && 'then' in out ) {
142 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.');
143 }
144 return out
145 } catch (e) {
146 event.endTime = Date.now();
147 event.error = e;
148 throw e
149 } finally {
150 dispatchEvent(event)
151 .catch( e => config.console.error('Failed to dispatch event', e));
152 }
153 }
154
155 function routerOptions({ sync }, ...args){
156 const { data, callback, name } = handler(...args);
157
158 const {event,childP} =
159 callback ? setupEvent({ parentEvent, name, data, sync }) : {};
160
161 if( callback && sync ) {
162 return handlerSync({ data, callback, childP, name, event })
163 } else if ( callback && !sync ) {
164 return handlerAsync({ data, callback, childP, name, event })
165 } else {
166 return handlerData({ data })
167 }
168 }
169
170 async function routerAsync(...args){
171 const out = await routerOptions({ sync: false }, ...args);
172 return out
173 }
174
175 function routerSync(...args){
176 const out = routerOptions({ sync: true }, ...args);
177 return out
178 }
179
180 routerAsync.sync = routerSync;
181 return routerAsync
182 }
183
184 let rootEvent = RootEvent();
185 let handlerInstance = Instance(rootEvent);
186 handlerInstance.flush = rootEvent.flush;
187 handlerInstance.config = config;
188 return handlerInstance
189 }
190
191 /**
192 * Safely invoke a callback even if trace is null.
193 *
194 * useful when there are multiple entry points into a function and some are not
195 * passing in a trace.
196 *
197 * @param {*} trace
198 * @param {...any} args
199 * @returns
200 */
201 function call(trace, ...args){
202 const cb = args.find( x => typeof x == 'function' );
203
204 if ( trace ) {
205 return trace( ...args )
206 } else if ( cb ) {
207 return cb( (...args) => call(null, ...args) )
208 }
209 return null
210 }
211
212 Main.call = call;
213
214 /**
215 * Create a no-op trace if provided trace is null
216 *
217 * Much like stagnant.call, useful when there are multiple entry points into a function and some are not
218 * passing in a trace.
219 *
220 * @param {*} trace
221 * @param {...any} args
222 * @returns
223 */
224 function ensure(trace){
225 if( trace ) {
226 return trace
227 } else {
228 return (...args) => call(null, ...args)
229 }
230 }
231
232 Main.ensure = ensure;
233
234 return Main;
235
236})));
237//# sourceMappingURL=stagnant.browser.js.map