UNPKG

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