1 | function defaultConfig(){
|
2 |
|
3 | function onevent(){}
|
4 | function onerror(){}
|
5 | function onsuccess(){}
|
6 | function onflush(){}
|
7 |
|
8 |
|
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 |
|
25 | function 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 |
|
187 |
|
188 |
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 | function 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 |
|
206 | Main.call = call
|
207 |
|
208 |
|
209 |
|
210 |
|
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 | function ensure(trace){
|
219 | if( trace ) {
|
220 | return trace
|
221 | } else {
|
222 | return (...args) => call(null, ...args)
|
223 | }
|
224 | }
|
225 |
|
226 | Main.ensure = ensure
|
227 | export default Main |
\ | No newline at end of file |