gate-executor 3.0.0 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ declare function MakeGateExecutor(options?: any): any;
2
+ export default MakeGateExecutor;
package/gate-executor.js CHANGED
@@ -1,12 +1,6 @@
1
- /* Copyright (c) 2014-2020 Richard Rodger, MIT License */
2
- /* $lab:coverage:off$ */
3
- 'use strict'
4
-
5
- // Core modules.
6
- var Assert = require('assert')
7
- Assert = 'function' === typeof Assert ? Assert : function () {}
8
- /* $lab:coverage:on$ */
9
-
1
+ "use strict";
2
+ /* Copyright (c) 2014-2021 Richard Rodger, MIT License */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
10
4
  // Create root instance. Exported as module.
11
5
  // * `options` (object): instance options as key-value pairs.
12
6
  //
@@ -14,351 +8,287 @@ Assert = 'function' === typeof Assert ? Assert : function () {}
14
8
  // * `interval` (integer): millisecond interval for timeout checks. Default: 111.
15
9
  // * `timeout` (integer): common millisecond timeout.
16
10
  // Can be overridden by work item options. Default: 2222.
17
- function make_GateExecutor(options) {
18
- options = options || {}
19
- options.interval = null == options.interval ? 111 : options.interval
20
- options.timeout = null == options.timeout ? 2222 : options.timeout
21
-
22
- Assert('object' === typeof options)
23
- Assert('number' === typeof options.interval)
24
- Assert('number' === typeof options.timeout)
25
- Assert(0 < options.interval)
26
- Assert(0 < options.timeout)
27
-
28
- return new GateExecutor(options, 0)
11
+ function MakeGateExecutor(options) {
12
+ options = options || {};
13
+ options.interval = null == options.interval ? 111 : options.interval;
14
+ options.timeout = null == options.timeout ? 2222 : options.timeout;
15
+ return GateExecutor(options, 0);
29
16
  }
30
-
31
17
  // Create a new instance.
32
18
  // * `options` (object): instance options as key-value pairs.
33
19
  // * `instance_counter` (integer): count number of instances created;
34
20
  // used as identifier.
35
21
  function GateExecutor(options, instance_counter) {
36
- var self = this
37
-
38
- Assert('object' === typeof options)
39
- Assert('number' === typeof instance_counter)
40
-
41
- self.id = ++instance_counter
42
- self.options = options
43
-
44
- // Work queue.
45
- var q = []
46
-
47
- // Work-in-progress set.
48
- var progress = {
49
- // Lookup work by id.
50
- lookup: {},
51
-
52
- // Work history - a list of work items in the order executed.
53
- history: [],
54
- }
55
-
56
- // List of work items to check for timeouts.
57
- var timeout_checklist = []
58
-
59
- // Internal state.
60
- var s = {
61
- // Count of work items added to this instance. Used as generated work identifier.
62
- work_counter: 0,
63
-
64
- // When `true`, the instance is in a gated state, and work cannot proceed
65
- // until the gated in-progress work item is completed.
66
- gate: false,
67
-
68
- // When `true`, the instance processes work items as they arrive.
69
- // When `false`, no processing happens, and the instance must be started by
70
- // calling the `start` method.
71
- running: false,
72
-
73
- // A function called when the work queue and work-in-progress set
74
- // are empty. Set by calling the `clear` method. Will be called
75
- // each time the instance empty.
76
- clear: null,
77
-
78
- // A function called once only when the work queue and
79
- // work-in-progress set are first emptied after each start. Set as
80
- // an optional argument to the `start` method.
81
- firstclear: null,
82
-
83
- // Timeout interval reference value returned by `setInterval`.
84
- // Timeouts are not checked using `setTimeout`, as it is more
85
- // efficient, and more than sufficient, to check timeouts periodically.
86
- tm_in: null,
87
- }
88
-
89
- // Process the next work item.
90
- function processor() {
91
- // If not running, don't process any work items.
92
- if (!s.running) {
93
- return
94
- }
95
-
96
- // The timeout interval check is stopped and started only as needed.
97
- if (!self.isclear() && !s.tm_in) {
98
- s.tm_in = setInterval(timeout_check, options.interval)
99
- }
100
-
101
- // Process the next work item, returning `true` if there was one.
102
- do {
103
- var next = false
104
- var work = null
105
-
106
- // Remove next work item from the front of the work queue.
107
- if (!s.gate) {
108
- work = q.shift()
109
- }
110
-
111
- if (work) {
112
- Assert('object' === typeof work)
113
- Assert('string' === typeof work.id)
114
- Assert('function' === typeof work.fn)
115
-
116
- // Add work item to the work-in-progress set.
117
- progress.lookup[work.id] = work
118
- progress.history.push(work)
119
-
120
- // If work item is a gate, set the state of the instance as
121
- // gated. This work item will need to complete before later
122
- // work items in the queue can be processed.
123
- s.gate = work.gate
124
-
125
- // Call the work item function (which does the real work),
126
- // passing a callback. This callback has no arguments
127
- // (including no error!). It is called only to indicate
128
- // completion of the work item. Work items must handle their
129
- // own errors and results.
130
- work.start = Date.now()
131
- work.callback = make_work_fn_callback(work)
132
-
133
- timeout_checklist.push(work)
134
- work.fn(work.callback)
135
-
136
- next = true
137
- }
138
- } while (next)
139
- // Keep processing work items until none are left or a gate is reached.
140
- }
141
-
142
- // Create the callback for the work function
143
- function make_work_fn_callback(work) {
144
- return function work_fn_callback() {
145
- if (work.done) {
146
- return
147
- }
148
-
149
- // Remove the work item from the work-in-progress set. As
150
- // work items may complete out of order, prune the history
151
- // from the front until the first incomplete work
152
- // item. Later complete work items will eventually be
153
- // reached on another processing round.
154
- work.done = true
155
- delete progress.lookup[work.id]
156
-
157
- while (progress.history[0] && progress.history[0].done) {
158
- progress.history.shift()
159
- }
160
-
161
- while (timeout_checklist[0] && timeout_checklist[0].done) {
162
- timeout_checklist.shift()
163
- }
164
-
165
- // If the work item was a gate, it is now complete, and the
166
- // instance can be ungated, allowing later work items in the
167
- // queue to be processed.
168
- if (work.gate) {
169
- s.gate = false
170
- }
171
-
172
- // If work queue and work-in-progress set are empty, then
173
- // call the registered clear functions.
174
- if (0 === q.length && 0 === progress.history.length) {
175
- clearInterval(s.tm_in)
176
- s.tm_in = null
177
-
178
- if (s.firstclear) {
179
- var fc = s.firstclear
180
- s.firstclear = null
181
- fc()
182
- }
183
-
184
- if (s.clear) {
185
- s.clear()
22
+ let self = {};
23
+ self.id = ++instance_counter;
24
+ self.options = options;
25
+ // Work queue.
26
+ let q = [];
27
+ // Work-in-progress set.
28
+ let progress = {
29
+ // Lookup work by id.
30
+ lookup: {},
31
+ // Work history - a list of work items in the order executed.
32
+ history: [],
33
+ };
34
+ // List of work items to check for timeouts.
35
+ let timeout_checklist = [];
36
+ // Internal state.
37
+ let s = {
38
+ // Count of work items added to this instance. Used as generated work identifier.
39
+ work_counter: 0,
40
+ // When `true`, the instance is in a gated state, and work cannot proceed
41
+ // until the gated in-progress work item is completed.
42
+ gate: false,
43
+ // When `true`, the instance processes work items as they arrive.
44
+ // When `false`, no processing happens, and the instance must be started by
45
+ // calling the `start` method.
46
+ running: false,
47
+ // A function called when the work queue and work-in-progress set
48
+ // are empty. Set by calling the `clear` method. Will be called
49
+ // each time the instance empty.
50
+ clear: null,
51
+ // A function called once only when the work queue and
52
+ // work-in-progress set are first emptied after each start. Set as
53
+ // an optional argument to the `start` method.
54
+ firstclear: null,
55
+ // Timeout interval reference value returned by `setInterval`.
56
+ // Timeouts are not checked using `setTimeout`, as it is more
57
+ // efficient, and more than sufficient, to check timeouts periodically.
58
+ tm_in: null,
59
+ hw_tmc: 0,
60
+ hw_hst: 0,
61
+ };
62
+ // Process the next work item.
63
+ function processor() {
64
+ // If not running, don't process any work items.
65
+ if (!s.running) {
66
+ return;
186
67
  }
187
- }
188
-
189
- // Process each work item on next tick to avoid lockups.
190
- setImmediate(processor)
191
- }
192
- }
193
-
194
- // To be run periodically via setInterval. For timed out work items,
195
- // calls the done callback to allow work queue to proceed, and marks
196
- // the work item as finished. Work items can receive notification of
197
- // timeouts by providing an `ontm` callback property in the
198
- // work definition object. Work items must handle timeout errors
199
- // themselves, gate-executor cares only for the fact that a timeout
200
- // happened, so it can continue processing.
201
- function timeout_check() {
202
- var now = Date.now()
203
- var work = null
204
-
205
- for (var i = 0; i < timeout_checklist.length; ++i) {
206
- work = timeout_checklist[i]
207
-
208
- if (!work.gate && !work.done && work.tm < now - work.start) {
209
- if (work.ontm) {
210
- work.ontm(work.tm, work.start, now)
68
+ // The timeout interval check is stopped and started only as needed.
69
+ if (!self.isclear() && !s.tm_in) {
70
+ s.tm_in = setInterval(timeout_check, options.interval);
211
71
  }
212
-
213
- work.callback()
214
- }
215
- }
216
- }
217
-
218
- // Start processing work items. Must be called to start processing.
219
- // Can be called at anytime, interspersed with calls to other
220
- // methods, including `add`. Takes a function as argument, which is
221
- // called only once on the next time the queues are clear.
222
- self.start = function (firstclear) {
223
- Assert(null == firstclear || 'function' === typeof firstclear)
224
-
225
- // Allow API chaining by not starting in current execution path.
226
- setImmediate(function () {
227
- s.running = true
228
-
229
- if (firstclear) {
230
- s.firstclear = firstclear
231
- }
232
-
233
- processor()
234
- })
235
-
236
- return self
237
- }
238
-
239
- // Pause the processing of work items. Newly added items, and items
240
- // not yet started, will not proceed, but items already in progress
241
- // will complete, and the clear function will be called once all in
242
- // progress items finish.
243
- self.pause = function () {
244
- s.running = false
245
- }
246
-
247
- // Submit a function that will be called each time there are no more
248
- // work items to process. Multiple calls to this method will replace
249
- // the previously registered clear function.
250
- self.clear = function (done) {
251
- Assert('function' === typeof done)
252
- s.clear = done
253
- return self
254
- }
255
-
256
- // Returns `true` when there are no more work items to process.
257
- self.isclear = function () {
258
- return 0 === q.length && 0 === progress.history.length
259
- }
260
-
261
- // Add a work item. This is an object with fields:
262
- // * `fn` (function): the function that performs the work. Takes a
263
- // single argument, the callback function to call when the work is
264
- // complete. THis callback does **not** accept errors or
265
- // results. It's only purpose is to indicate that the work is
266
- // complete (whether failed or not). The work function itself must
267
- // handle callbacks to the application. Required.
268
- // * `id` (string): identifier for the work item. Optional.
269
- // * `tm` (integer): millisecond timeout specific to this work item,
270
- // overrides general timeout. Optional.
271
- // * `ontm` (function): callback to indicate work item timeout. Optional.
272
- // * `dn` (string): description of the work item, used in the
273
- // state description. Optional.
274
- self.add = function (work) {
275
- Assert('object' === typeof work)
276
- Assert('function' === typeof work.fn)
277
- Assert(null == work.id || 'string' === typeof work.id)
278
- Assert(null == work.tm || 'number' === typeof work.tm)
279
- Assert(null == work.dn || 'string' === typeof work.dn)
280
-
281
- s.work_counter += 1
282
- work.id = work.id || '' + s.work_counter
283
- work.ge = self.id
284
- work.tm = null == work.tm ? options.timeout : work.tm
285
-
286
- work.dn = work.dn || work.fn.name || '' + Date.now()
287
-
288
- // Used by calling code to store additional context.
289
- work.ctxt = {}
290
-
291
- q.push(work)
292
-
293
- if (s.running) {
294
- // Work items are **not** processed in the current execution path!
295
- // This prevents lockup, and avoids false positives in unit tests.
296
- // Work items are assumed to be inherently asynchronous.
297
- setImmediate(processor)
298
- }
299
-
300
- return self
301
- }
302
-
303
- // Create a new gate. Returns a new `GateExecutor` instance. All
304
- // work items added to the new instance must complete before the
305
- // gate is cleared, and work items in the queue can be processed. A
306
- // gate is cleared when the new instance is **first** cleared. Work
307
- // items subsequently added to the new instance are not considered
308
- // part of the gate. Gates can extend to any depth and form a tree
309
- // structure that requires breadth-first traversal in terms of the
310
- // work item queue. Gates do not have timeouts, and can only be
311
- // cleared when all added work items complete.
312
- self.gate = function () {
313
- var ge = new GateExecutor(options, instance_counter)
314
-
315
- var fn = function gate(done) {
316
- // This is the work function of the gate, which starts the new
317
- // instance, and considers the gate work item complete when the
318
- // work queue clears for the first time.
319
- ge.start(done)
72
+ // Process the next work item, returning `true` if there was one.
73
+ let next = false;
74
+ do {
75
+ next = false;
76
+ let work = null;
77
+ // Remove next work item from the front of the work queue.
78
+ if (!s.gate) {
79
+ work = q.shift();
80
+ }
81
+ if (work) {
82
+ // Add work item to the work-in-progress set.
83
+ progress.lookup[work.id] = work;
84
+ progress.history.push(work);
85
+ s.hw_hst =
86
+ progress.history.length > s.hw_hst ? progress.history.length : s.hw_hst;
87
+ // If work item is a gate, set the state of the instance as
88
+ // gated. This work item will need to complete before later
89
+ // work items in the queue can be processed.
90
+ s.gate = work.gate;
91
+ // Call the work item function (which does the real work),
92
+ // passing a callback. This callback has no arguments
93
+ // (including no error!). It is called only to indicate
94
+ // completion of the work item. Work items must handle their
95
+ // own errors and results.
96
+ work.start = Date.now();
97
+ work.callback = make_work_fn_callback(work);
98
+ timeout_checklist.push(work);
99
+ s.hw_tmc =
100
+ timeout_checklist.length > s.hw_tmc ? timeout_checklist.length : s.hw_tmc;
101
+ work.fn(work.callback);
102
+ next = true;
103
+ }
104
+ } while (next);
105
+ // Keep processing work items until none are left or a gate is reached.
320
106
  }
321
-
322
- self.add({ gate: ge, fn: fn })
323
-
324
- return ge
325
- }
326
-
327
- // Return a data structure describing the current state of the work
328
- // queues, and organised as a tree structure indicating the gating
329
- // relationships.
330
- self.state = function () {
331
- var out = []
332
-
333
- // First list any in-progress work items.
334
- for (var hI = 0; hI < progress.history.length; ++hI) {
335
- var pe = progress.history[hI]
336
- if (!pe.done) {
337
- out.push({ s: 'a', ge: pe.ge, dn: pe.dn, id: pe.id })
338
- }
107
+ // Create the callback for the work function
108
+ function make_work_fn_callback(work) {
109
+ return function work_fn_callback() {
110
+ if (work.done) {
111
+ return;
112
+ }
113
+ work.end = Date.now();
114
+ // Remove the work item from the work-in-progress set. As
115
+ // work items may complete out of order, prune the history
116
+ // from the front until the first incomplete work
117
+ // item. Later complete work items will eventually be
118
+ // reached on another processing round.
119
+ work.done = true;
120
+ delete progress.lookup[work.id];
121
+ while (progress.history[0] && progress.history[0].done) {
122
+ progress.history.shift();
123
+ }
124
+ while (timeout_checklist[0] && timeout_checklist[0].done) {
125
+ timeout_checklist.shift();
126
+ }
127
+ // If the work item was a gate, it is now complete, and the
128
+ // instance can be ungated, allowing later work items in the
129
+ // queue to be processed.
130
+ if (work.gate) {
131
+ s.gate = false;
132
+ }
133
+ // If work queue and work-in-progress set are empty, then
134
+ // call the registered clear functions.
135
+ if (0 === q.length && 0 === progress.history.length) {
136
+ clearInterval(s.tm_in);
137
+ s.tm_in = null;
138
+ if (s.firstclear) {
139
+ let fc = s.firstclear;
140
+ s.firstclear = null;
141
+ fc();
142
+ }
143
+ if (s.clear) {
144
+ s.clear();
145
+ }
146
+ }
147
+ // Process each work item on next tick to avoid lockups.
148
+ setImmediate(processor);
149
+ };
339
150
  }
340
-
341
- // Then list any waiting work items.
342
- for (var qI = 0; qI < q.length; ++qI) {
343
- var qe = q[qI]
344
- if (qe.gate) {
345
- // Go down a level when there's a gate.
346
- out.push(qe.gate.state())
347
- } else {
348
- out.push({ s: 'w', ge: qe.ge, dn: qe.dn, id: qe.id })
349
- }
350
- }
351
-
352
- out.internal = {
353
- qlen: q.length,
354
- hlen: progress.history.length,
355
- klen: Object.keys(progress.lookup).length,
356
- tlen: timeout_check.length,
151
+ // To be run periodically via setInterval. For timed out work items,
152
+ // calls the done callback to allow work queue to proceed, and marks
153
+ // the work item as finished. Work items can receive notification of
154
+ // timeouts by providing an `ontm` callback property in the
155
+ // work definition object. Work items must handle timeout errors
156
+ // themselves, gate-executor cares only for the fact that a timeout
157
+ // happened, so it can continue processing.
158
+ function timeout_check() {
159
+ let now = Date.now();
160
+ let work = null;
161
+ for (let i = 0; i < timeout_checklist.length; ++i) {
162
+ work = timeout_checklist[i];
163
+ if (!work.gate && !work.done && work.tm < now - work.start) {
164
+ if (work.ontm) {
165
+ work.ontm(work.tm, work.start, now);
166
+ }
167
+ work.callback();
168
+ }
169
+ }
357
170
  }
358
-
359
- return out
360
- }
171
+ // Start processing work items. Must be called to start processing.
172
+ // Can be called at anytime, interspersed with calls to other
173
+ // methods, including `add`. Takes a function as argument, which is
174
+ // called only once on the next time the queues are clear.
175
+ self.start = function (firstclear) {
176
+ // Allow API chaining by not starting in current execution path.
177
+ setImmediate(function () {
178
+ s.running = true;
179
+ if (firstclear) {
180
+ s.firstclear = firstclear;
181
+ }
182
+ processor();
183
+ });
184
+ return self;
185
+ };
186
+ // Pause the processing of work items. Newly added items, and items
187
+ // not yet started, will not proceed, but items already in progress
188
+ // will complete, and the clear function will be called once all in
189
+ // progress items finish.
190
+ self.pause = function () {
191
+ s.running = false;
192
+ };
193
+ // Submit a function that will be called each time there are no more
194
+ // work items to process. Multiple calls to this method will replace
195
+ // the previously registered clear function.
196
+ self.clear = function (done) {
197
+ s.clear = done;
198
+ return self;
199
+ };
200
+ // Returns `true` when there are no more work items to process.
201
+ self.isclear = function () {
202
+ return 0 === q.length && 0 === progress.history.length;
203
+ };
204
+ // Add a work item. This is an object with fields:
205
+ // * `fn` (function): the function that performs the work. Takes a
206
+ // single argument, the callback function to call when the work is
207
+ // complete. THis callback does **not** accept errors or
208
+ // results. It's only purpose is to indicate that the work is
209
+ // complete (whether failed or not). The work function itself must
210
+ // handle callbacks to the application. Required.
211
+ // * `id` (string): identifier for the work item. Optional.
212
+ // * `tm` (integer): millisecond timeout specific to this work item,
213
+ // overrides general timeout. Optional.
214
+ // * `ontm` (function): callback to indicate work item timeout. Optional.
215
+ // * `dn` (string): description of the work item, used in the
216
+ // state description. Optional.
217
+ self.add = function (work) {
218
+ s.work_counter += 1;
219
+ work.id = work.id || '' + s.work_counter;
220
+ work.ge = self.id;
221
+ work.tm = null == work.tm ? options.timeout : work.tm;
222
+ work.dn = work.dn || work.fn.name || '' + Date.now();
223
+ // Used by calling code to store additional context.
224
+ work.ctxt = {};
225
+ q.push(work);
226
+ if (s.running) {
227
+ // Work items are **not** processed in the current execution path!
228
+ // This prevents lockup, and avoids false positives in unit tests.
229
+ // Work items are assumed to be inherently asynchronous.
230
+ setImmediate(processor);
231
+ }
232
+ return self;
233
+ };
234
+ // Create a new gate. Returns a new `GateExecutor` instance. All
235
+ // work items added to the new instance must complete before the
236
+ // gate is cleared, and work items in the queue can be processed. A
237
+ // gate is cleared when the new instance is **first** cleared. Work
238
+ // items subsequently added to the new instance are not considered
239
+ // part of the gate. Gates can extend to any depth and form a tree
240
+ // structure that requires breadth-first traversal in terms of the
241
+ // work item queue. Gates do not have timeouts, and can only be
242
+ // cleared when all added work items complete.
243
+ self.gate = function () {
244
+ let ge = GateExecutor(options, instance_counter);
245
+ let fn = function gate(done) {
246
+ // This is the work function of the gate, which starts the new
247
+ // instance, and considers the gate work item complete when the
248
+ // work queue clears for the first time.
249
+ ge.start(done);
250
+ };
251
+ self.add({ gate: ge, fn: fn });
252
+ return ge;
253
+ };
254
+ // Return a data structure describing the current state of the work
255
+ // queues, and organised as a tree structure indicating the gating
256
+ // relationships.
257
+ self.state = function () {
258
+ let out = [];
259
+ // First list any in-progress work items.
260
+ for (let hI = 0; hI < progress.history.length; ++hI) {
261
+ let pe = progress.history[hI];
262
+ if (!pe.done) {
263
+ out.push({ s: 'a', ge: pe.ge, dn: pe.dn, id: pe.id });
264
+ }
265
+ }
266
+ // Then list any waiting work items.
267
+ for (let qI = 0; qI < q.length; ++qI) {
268
+ let qe = q[qI];
269
+ if (qe.gate) {
270
+ // Go down a level when there's a gate.
271
+ out.push(qe.gate.state());
272
+ }
273
+ else {
274
+ out.push({ s: 'w', ge: qe.ge, dn: qe.dn, id: qe.id });
275
+ }
276
+ }
277
+ out.internal = {
278
+ qlen: q.length,
279
+ hlen: progress.history.length,
280
+ klen: Object.keys(progress.lookup).length,
281
+ tlen: timeout_check.length,
282
+ hw_hst: s.hw_hst,
283
+ hw_tmc: s.hw_tmc,
284
+ };
285
+ return out;
286
+ };
287
+ return self;
361
288
  }
362
-
363
289
  // The module function
364
- module.exports = make_GateExecutor
290
+ exports.default = MakeGateExecutor;
291
+ if (undefined != typeof (module.exports)) {
292
+ module.exports = MakeGateExecutor;
293
+ }
294
+ //# sourceMappingURL=gate-executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate-executor.js","sourceRoot":"","sources":["gate-executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).GateExecutor=t()}}((function(){var t,e,n,r,i=(t=function(t,e){(function(t,n){(function(){var r=o.nextTick,i=(Function.prototype.apply,Array.prototype.slice),l={},u=0;function c(t,e){this._id=t,this._clearFn=e}c.prototype.unref=c.prototype.ref=function(){},c.prototype.close=function(){this._clearFn.call(window,this._id)},e.setImmediate="function"==typeof t?t:function(t){var n=u++,o=!(arguments.length<2)&&i.call(arguments,1);return l[n]=!0,r((function(){l[n]&&(o?t.apply(null,o):t.call(null),e.clearImmediate(n))})),n},e.clearImmediate="function"==typeof n?n:function(t){delete l[t]}}).call(this)}).call(this,i({}).setImmediate,i({}).clearImmediate)},function(n){return e||t(e={exports:{},parent:n},e.exports),e.exports}),o={},l=o={};function u(){throw new Error("setTimeout has not been defined")}function c(){throw new Error("clearTimeout has not been defined")}function a(t){if(n===setTimeout)return setTimeout(t,0);if((n===u||!n)&&setTimeout)return n=setTimeout,setTimeout(t,0);try{return n(t,0)}catch(e){try{return n.call(null,t,0)}catch(e){return n.call(this,t,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:u}catch(e){n=u}try{r="function"==typeof clearTimeout?clearTimeout:c}catch(e){r=c}}();var s,f=[],h=!1,d=-1;function p(){h&&s&&(h=!1,s.length?f=s.concat(f):d=-1,f.length&&m())}function m(){if(!h){var t=a(p);h=!0;for(var n=f.length;n;){for(s=f,f=[];++d<n;)s&&s[d].run();d=-1,n=f.length}s=null,h=!1,function(t){if(r===clearTimeout)return clearTimeout(t);if((r===c||!r)&&clearTimeout)return r=clearTimeout,clearTimeout(t);try{r(t)}catch(e){try{return r.call(null,t)}catch(e){return r.call(this,t)}}}(t)}}function g(t,e){this.fun=t,this.array=e}function y(){}l.nextTick=function(t){var e=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)e[n-1]=arguments[n];f.push(new g(t,e)),1!==f.length||h||a(m)},g.prototype.run=function(){this.fun.apply(null,this.array)},l.title="browser",l.browser=!0,l.env={},l.argv=[],l.version="",l.versions={},l.on=y,l.addListener=y,l.once=y,l.off=y,l.removeListener=y,l.removeAllListeners=y,l.emit=y,l.prependListener=y,l.prependOnceListener=y,l.listeners=function(t){return[]},l.binding=function(t){throw new Error("process.binding is not supported")},l.cwd=function(){return"/"},l.chdir=function(t){throw new Error("process.chdir is not supported")},l.umask=function(){return 0};var w={};return function(t){(function(){"use strict";function e(e){return(e=e||{}).interval=null==e.interval?111:e.interval,e.timeout=null==e.timeout?2222:e.timeout,function e(n,r){let i={};i.id=++r,i.options=n;let o=[],l={lookup:{},history:[]},u=[],c={work_counter:0,gate:!1,running:!1,clear:null,firstclear:null,tm_in:null,hw_tmc:0,hw_hst:0};function a(){if(!c.running)return;i.isclear()||c.tm_in||(c.tm_in=setInterval(f,n.interval));let t=!1;do{t=!1;let e=null;c.gate||(e=o.shift()),e&&(l.lookup[e.id]=e,l.history.push(e),c.hw_hst=l.history.length>c.hw_hst?l.history.length:c.hw_hst,c.gate=e.gate,e.start=Date.now(),e.callback=s(e),u.push(e),c.hw_tmc=u.length>c.hw_tmc?u.length:c.hw_tmc,e.fn(e.callback),t=!0)}while(t)}function s(e){return function(){if(!e.done){for(e.end=Date.now(),e.done=!0,delete l.lookup[e.id];l.history[0]&&l.history[0].done;)l.history.shift();for(;u[0]&&u[0].done;)u.shift();if(e.gate&&(c.gate=!1),0===o.length&&0===l.history.length){if(clearInterval(c.tm_in),c.tm_in=null,c.firstclear){let t=c.firstclear;c.firstclear=null,t()}c.clear&&c.clear()}t(a)}}}function f(){let t=Date.now(),e=null;for(let n=0;n<u.length;++n)e=u[n],!e.gate&&!e.done&&e.tm<t-e.start&&(e.ontm&&e.ontm(e.tm,e.start,t),e.callback())}return i.start=function(e){return t((function(){c.running=!0,e&&(c.firstclear=e),a()})),i},i.pause=function(){c.running=!1},i.clear=function(t){return c.clear=t,i},i.isclear=function(){return 0===o.length&&0===l.history.length},i.add=function(e){return c.work_counter+=1,e.id=e.id||""+c.work_counter,e.ge=i.id,e.tm=null==e.tm?n.timeout:e.tm,e.dn=e.dn||e.fn.name||""+Date.now(),e.ctxt={},o.push(e),c.running&&t(a),i},i.gate=function(){let t=e(n,r);return i.add({gate:t,fn:function(e){t.start(e)}}),t},i.state=function(){let t=[];for(let e=0;e<l.history.length;++e){let n=l.history[e];n.done||t.push({s:"a",ge:n.ge,dn:n.dn,id:n.id})}for(let e=0;e<o.length;++e){let n=o[e];n.gate?t.push(n.gate.state()):t.push({s:"w",ge:n.ge,dn:n.dn,id:n.id})}return t.internal={qlen:o.length,hlen:l.history.length,klen:Object.keys(l.lookup).length,tlen:f.length,hw_hst:c.hw_hst,hw_tmc:c.hw_tmc},t},i}(e,0)}Object.defineProperty(w,"__esModule",{value:!0}),w.default=e,null!=typeof w&&(w=e)}).call(this)}.call(this,i({}).setImmediate),w}));
@@ -0,0 +1,374 @@
1
+ /* Copyright (c) 2014-2021 Richard Rodger, MIT License */
2
+
3
+
4
+ type Work = {
5
+ id: string // Identifier
6
+ ge: string // GateExecutor identifier
7
+ tm: number // Timeout
8
+ dn: string // Description
9
+ fn: (callback: () => void) => void
10
+ start: number
11
+ end: number
12
+ gate?: any
13
+ callback: () => void
14
+ ctxt: { [key: string]: any }
15
+ }
16
+
17
+
18
+
19
+ // Create root instance. Exported as module.
20
+ // * `options` (object): instance options as key-value pairs.
21
+ //
22
+ // The options are:
23
+ // * `interval` (integer): millisecond interval for timeout checks. Default: 111.
24
+ // * `timeout` (integer): common millisecond timeout.
25
+ // Can be overridden by work item options. Default: 2222.
26
+ function MakeGateExecutor(options?: any) {
27
+ options = options || {}
28
+ options.interval = null == options.interval ? 111 : options.interval
29
+ options.timeout = null == options.timeout ? 2222 : options.timeout
30
+
31
+ return GateExecutor(options, 0)
32
+ }
33
+
34
+ // Create a new instance.
35
+ // * `options` (object): instance options as key-value pairs.
36
+ // * `instance_counter` (integer): count number of instances created;
37
+ // used as identifier.
38
+ function GateExecutor(this: any, options: any, instance_counter: number) {
39
+ let self: any = {}
40
+
41
+ self.id = ++instance_counter
42
+ self.options = options
43
+
44
+ // Work queue.
45
+ let q: Work[] = []
46
+
47
+ // Work-in-progress set.
48
+ let progress: any = {
49
+ // Lookup work by id.
50
+ lookup: {},
51
+
52
+ // Work history - a list of work items in the order executed.
53
+ history: [],
54
+ }
55
+
56
+ // List of work items to check for timeouts.
57
+ let timeout_checklist: any[] = []
58
+
59
+ // Internal state.
60
+ let s: any = {
61
+ // Count of work items added to this instance. Used as generated work identifier.
62
+ work_counter: 0,
63
+
64
+ // When `true`, the instance is in a gated state, and work cannot proceed
65
+ // until the gated in-progress work item is completed.
66
+ gate: false,
67
+
68
+ // When `true`, the instance processes work items as they arrive.
69
+ // When `false`, no processing happens, and the instance must be started by
70
+ // calling the `start` method.
71
+ running: false,
72
+
73
+ // A function called when the work queue and work-in-progress set
74
+ // are empty. Set by calling the `clear` method. Will be called
75
+ // each time the instance empty.
76
+ clear: null,
77
+
78
+ // A function called once only when the work queue and
79
+ // work-in-progress set are first emptied after each start. Set as
80
+ // an optional argument to the `start` method.
81
+ firstclear: null,
82
+
83
+ // Timeout interval reference value returned by `setInterval`.
84
+ // Timeouts are not checked using `setTimeout`, as it is more
85
+ // efficient, and more than sufficient, to check timeouts periodically.
86
+ tm_in: null,
87
+
88
+ hw_tmc: 0,
89
+ hw_hst: 0,
90
+
91
+ }
92
+
93
+
94
+
95
+ // Process the next work item.
96
+ function processor() {
97
+ // If not running, don't process any work items.
98
+ if (!s.running) {
99
+ return
100
+ }
101
+
102
+ // The timeout interval check is stopped and started only as needed.
103
+ if (!self.isclear() && !s.tm_in) {
104
+ s.tm_in = setInterval(timeout_check, options.interval)
105
+ }
106
+
107
+ // Process the next work item, returning `true` if there was one.
108
+ let next = false
109
+ do {
110
+ next = false
111
+ let work = null
112
+
113
+ // Remove next work item from the front of the work queue.
114
+ if (!s.gate) {
115
+ work = q.shift()
116
+ }
117
+
118
+ if (work) {
119
+ // Add work item to the work-in-progress set.
120
+ progress.lookup[work.id] = work
121
+ progress.history.push(work)
122
+
123
+ s.hw_hst =
124
+ progress.history.length > s.hw_hst ? progress.history.length : s.hw_hst
125
+
126
+ // If work item is a gate, set the state of the instance as
127
+ // gated. This work item will need to complete before later
128
+ // work items in the queue can be processed.
129
+ s.gate = work.gate
130
+
131
+ // Call the work item function (which does the real work),
132
+ // passing a callback. This callback has no arguments
133
+ // (including no error!). It is called only to indicate
134
+ // completion of the work item. Work items must handle their
135
+ // own errors and results.
136
+ work.start = Date.now()
137
+ work.callback = make_work_fn_callback(work)
138
+
139
+ timeout_checklist.push(work)
140
+ s.hw_tmc =
141
+ timeout_checklist.length > s.hw_tmc ? timeout_checklist.length : s.hw_tmc
142
+
143
+ work.fn(work.callback)
144
+
145
+ next = true
146
+ }
147
+ } while (next)
148
+ // Keep processing work items until none are left or a gate is reached.
149
+ }
150
+
151
+ // Create the callback for the work function
152
+ function make_work_fn_callback(work: any) {
153
+ return function work_fn_callback() {
154
+ if (work.done) {
155
+ return
156
+ }
157
+
158
+ work.end = Date.now()
159
+
160
+ // Remove the work item from the work-in-progress set. As
161
+ // work items may complete out of order, prune the history
162
+ // from the front until the first incomplete work
163
+ // item. Later complete work items will eventually be
164
+ // reached on another processing round.
165
+ work.done = true
166
+ delete progress.lookup[work.id]
167
+
168
+ while (progress.history[0] && progress.history[0].done) {
169
+ progress.history.shift()
170
+ }
171
+
172
+ while (timeout_checklist[0] && timeout_checklist[0].done) {
173
+ timeout_checklist.shift()
174
+ }
175
+
176
+ // If the work item was a gate, it is now complete, and the
177
+ // instance can be ungated, allowing later work items in the
178
+ // queue to be processed.
179
+ if (work.gate) {
180
+ s.gate = false
181
+ }
182
+
183
+ // If work queue and work-in-progress set are empty, then
184
+ // call the registered clear functions.
185
+ if (0 === q.length && 0 === progress.history.length) {
186
+ clearInterval(s.tm_in)
187
+ s.tm_in = null
188
+
189
+ if (s.firstclear) {
190
+ let fc = s.firstclear
191
+ s.firstclear = null
192
+ fc()
193
+ }
194
+
195
+ if (s.clear) {
196
+ s.clear()
197
+ }
198
+ }
199
+
200
+ // Process each work item on next tick to avoid lockups.
201
+ setImmediate(processor)
202
+ }
203
+ }
204
+
205
+ // To be run periodically via setInterval. For timed out work items,
206
+ // calls the done callback to allow work queue to proceed, and marks
207
+ // the work item as finished. Work items can receive notification of
208
+ // timeouts by providing an `ontm` callback property in the
209
+ // work definition object. Work items must handle timeout errors
210
+ // themselves, gate-executor cares only for the fact that a timeout
211
+ // happened, so it can continue processing.
212
+ function timeout_check() {
213
+ let now = Date.now()
214
+ let work = null
215
+
216
+ for (let i = 0; i < timeout_checklist.length; ++i) {
217
+ work = timeout_checklist[i]
218
+
219
+ if (!work.gate && !work.done && work.tm < now - work.start) {
220
+ if (work.ontm) {
221
+ work.ontm(work.tm, work.start, now)
222
+ }
223
+
224
+ work.callback()
225
+ }
226
+ }
227
+ }
228
+
229
+ // Start processing work items. Must be called to start processing.
230
+ // Can be called at anytime, interspersed with calls to other
231
+ // methods, including `add`. Takes a function as argument, which is
232
+ // called only once on the next time the queues are clear.
233
+ self.start = function(firstclear: any) {
234
+ // Allow API chaining by not starting in current execution path.
235
+ setImmediate(function() {
236
+ s.running = true
237
+
238
+ if (firstclear) {
239
+ s.firstclear = firstclear
240
+ }
241
+
242
+ processor()
243
+ })
244
+
245
+ return self
246
+ }
247
+
248
+ // Pause the processing of work items. Newly added items, and items
249
+ // not yet started, will not proceed, but items already in progress
250
+ // will complete, and the clear function will be called once all in
251
+ // progress items finish.
252
+ self.pause = function() {
253
+ s.running = false
254
+ }
255
+
256
+ // Submit a function that will be called each time there are no more
257
+ // work items to process. Multiple calls to this method will replace
258
+ // the previously registered clear function.
259
+ self.clear = function(done: any) {
260
+ s.clear = done
261
+ return self
262
+ }
263
+
264
+ // Returns `true` when there are no more work items to process.
265
+ self.isclear = function() {
266
+ return 0 === q.length && 0 === progress.history.length
267
+ }
268
+
269
+ // Add a work item. This is an object with fields:
270
+ // * `fn` (function): the function that performs the work. Takes a
271
+ // single argument, the callback function to call when the work is
272
+ // complete. THis callback does **not** accept errors or
273
+ // results. It's only purpose is to indicate that the work is
274
+ // complete (whether failed or not). The work function itself must
275
+ // handle callbacks to the application. Required.
276
+ // * `id` (string): identifier for the work item. Optional.
277
+ // * `tm` (integer): millisecond timeout specific to this work item,
278
+ // overrides general timeout. Optional.
279
+ // * `ontm` (function): callback to indicate work item timeout. Optional.
280
+ // * `dn` (string): description of the work item, used in the
281
+ // state description. Optional.
282
+ self.add = function(work: Work) {
283
+ s.work_counter += 1
284
+ work.id = work.id || '' + s.work_counter
285
+ work.ge = self.id
286
+ work.tm = null == work.tm ? options.timeout : work.tm
287
+
288
+ work.dn = work.dn || work.fn.name || '' + Date.now()
289
+
290
+ // Used by calling code to store additional context.
291
+ work.ctxt = {}
292
+
293
+ q.push(work)
294
+
295
+ if (s.running) {
296
+ // Work items are **not** processed in the current execution path!
297
+ // This prevents lockup, and avoids false positives in unit tests.
298
+ // Work items are assumed to be inherently asynchronous.
299
+ setImmediate(processor)
300
+ }
301
+
302
+ return self
303
+ }
304
+
305
+ // Create a new gate. Returns a new `GateExecutor` instance. All
306
+ // work items added to the new instance must complete before the
307
+ // gate is cleared, and work items in the queue can be processed. A
308
+ // gate is cleared when the new instance is **first** cleared. Work
309
+ // items subsequently added to the new instance are not considered
310
+ // part of the gate. Gates can extend to any depth and form a tree
311
+ // structure that requires breadth-first traversal in terms of the
312
+ // work item queue. Gates do not have timeouts, and can only be
313
+ // cleared when all added work items complete.
314
+ self.gate = function() {
315
+ let ge: any = GateExecutor(options, instance_counter)
316
+
317
+ let fn = function gate(done: any) {
318
+ // This is the work function of the gate, which starts the new
319
+ // instance, and considers the gate work item complete when the
320
+ // work queue clears for the first time.
321
+ ge.start(done)
322
+ }
323
+
324
+ self.add({ gate: ge, fn: fn })
325
+
326
+ return ge
327
+ }
328
+
329
+ // Return a data structure describing the current state of the work
330
+ // queues, and organised as a tree structure indicating the gating
331
+ // relationships.
332
+ self.state = function() {
333
+ let out: any = []
334
+
335
+ // First list any in-progress work items.
336
+ for (let hI = 0; hI < progress.history.length; ++hI) {
337
+ let pe = progress.history[hI]
338
+ if (!pe.done) {
339
+ out.push({ s: 'a', ge: pe.ge, dn: pe.dn, id: pe.id })
340
+ }
341
+ }
342
+
343
+ // Then list any waiting work items.
344
+ for (let qI = 0; qI < q.length; ++qI) {
345
+ let qe = q[qI]
346
+ if (qe.gate) {
347
+ // Go down a level when there's a gate.
348
+ out.push(qe.gate.state())
349
+ } else {
350
+ out.push({ s: 'w', ge: qe.ge, dn: qe.dn, id: qe.id })
351
+ }
352
+ }
353
+
354
+ out.internal = {
355
+ qlen: q.length,
356
+ hlen: progress.history.length,
357
+ klen: Object.keys(progress.lookup).length,
358
+ tlen: timeout_check.length,
359
+ hw_hst: s.hw_hst,
360
+ hw_tmc: s.hw_tmc,
361
+ }
362
+
363
+ return out
364
+ }
365
+
366
+ return self
367
+ }
368
+
369
+ // The module function
370
+ export default MakeGateExecutor
371
+
372
+ if (undefined != typeof (module.exports)) {
373
+ module.exports = MakeGateExecutor
374
+ }
package/package.json CHANGED
@@ -1,18 +1,22 @@
1
1
  {
2
2
  "name": "gate-executor",
3
- "version": "3.0.0",
3
+ "version": "3.2.1",
4
4
  "description": "A work queue that can be gated, stopping to wait for sub-queues to complete.",
5
5
  "main": "gate-executor.js",
6
- "browser": "dist/gate-executor.min.js",
6
+ "browser": "gate-executor.min.js",
7
+ "type": "commonjs",
8
+ "types": "gate-executor.d.ts",
7
9
  "scripts": {
8
- "test": "lab -v -P test -t 100 --shuffle -L -I document -r console -o stdout -r html -o test/coverage.html",
9
- "test-web": "browserify -o test-web/get.js -e test/gate-executor.test.js -s GateExecutor -im -i assert -i @hapi/lab && open test-web/index.html",
10
- "build": "browserify -o dist/gate-executor.min.js -e gate-executor.js -s GateExecutor -im -i assert -i @hapi/lab -p tinyify",
11
- "prettier": "prettier --write gate-executor.js",
12
- "coveralls": "lab -s -P test -r lcov | coveralls",
13
- "clean-npm": "rm -rf node_modules package-lock.json",
14
- "repo-tag": "REPO_VERSION=`node -e \"console.log(require('./package').version)\"` && echo TAG: v$REPO_VERSION && git commit -a -m v$REPO_VERSION && git push && git tag v$REPO_VERSION && git push --tags",
15
- "repo-publish": "npm run clean-npm && npm i --registry http://registry.npmjs.org && npm run prettier && npm test && npm run build && npm run test-web && npm run repo-tag && npm publish --access public --registry http://registry.npmjs.org"
10
+ "test": "jest --coverage --no-cache",
11
+ "test-some": "jest --no-cache -t",
12
+ "test-watch": "jest --coverage --watchAll",
13
+ "watch": "tsc -w -d",
14
+ "build": "tsc -d && cp gate-executor.js gate-executor.min.js && browserify -o gate-executor.min.js -e gate-executor.js -s @GateExecutor -im -i assert -p tinyify",
15
+ "reset": "npm run clean && npm i && npm run build && npm test",
16
+ "clean": "rm -rf node_modules yarn.lock package-lock.json",
17
+ "repo-tag": "REPO_VERSION=`node -e \"console.log(require('./package').version)\"` && echo TAG: v$REPO_VERSION && git commit -a -m v$REPO_VERSION && git push && git tag v$REPO_VERSION && git push --tags;",
18
+ "repo-publish": "npm run clean && npm i && npm run repo-publish-quick",
19
+ "repo-publish-quick": "npm run build && npm run test && npm run repo-tag && npm publish --access public --registry https://registry.npmjs.org "
16
20
  },
17
21
  "repository": {
18
22
  "type": "git",
@@ -26,13 +30,12 @@
26
30
  "files": [
27
31
  "LICENSE",
28
32
  "README.md",
29
- "gate-executor.js",
30
- "dist"
33
+ "gate-executor.*"
31
34
  ],
32
35
  "engines": {
33
- "node": ">=8.0.0"
36
+ "node": ">=12.0.0"
34
37
  },
35
- "author": "Richard Rodger (http://richardrodger.com)",
38
+ "author": "Richard Rodger (https://richardrodger.com)",
36
39
  "contributors": [
37
40
  "Jacob Pruitt (http://javascriptjake.com)",
38
41
  "Wyatt Preul (http://jsgeek.com)"
@@ -44,13 +47,14 @@
44
47
  "homepage": "https://github.com/senecajs/gate-executor",
45
48
  "dependencies": {},
46
49
  "devDependencies": {
47
- "@hapi/code": "^8.0.2",
48
- "@hapi/lab": "^23.0.0",
49
- "browserify": "^16.5.2",
50
- "coveralls": "^3.1.0",
51
- "detective": "^5.2.0",
52
- "prettier": "^2.1.0",
53
- "tinyify": "^3.0.0",
54
- "util.promisify": "^1.0.1"
50
+ "@types/jest": "^30.0.0",
51
+ "browserify": "^17.0.1",
52
+ "esbuild": "^0.27.1",
53
+ "esbuild-jest": "^0.5.0",
54
+ "jest": "^30.2.0",
55
+ "tinyify": "^4.0.0",
56
+ "ts-jest": "^29.4.6",
57
+ "typescript": "^5.9.3",
58
+ "util.promisify": "^1.1.3"
55
59
  }
56
60
  }
@@ -1 +0,0 @@
1
- !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).GateExecutor=t()}}((function(){var t={exports:{}};return function(e){!function(n){"object"==typeof t.exports?t.exports=n():("undefined"!=typeof window?window:void 0!==e?e:"undefined"!=typeof self?self:this).GateExecutor=n()}((function(){var t={exports:{}};return function(e){!function(n){"object"==typeof t.exports?t.exports=n():("undefined"!=typeof window?window:void 0!==e?e:"undefined"!=typeof self?self:this).GateExecutor=n()}((function(){var t={exports:{}};return function(e){!function(n){"object"==typeof t.exports?t.exports=n():("undefined"!=typeof window?window:void 0!==e?e:"undefined"!=typeof self?self:this).GateExecutor=n()}((function(){var t,e,n,o,i=(t=function(t,e){(function(t,n){var o=u.nextTick,i=(Function.prototype.apply,Array.prototype.slice),r={},f=0;function l(t,e){this._id=t,this._clearFn=e}l.prototype.unref=l.prototype.ref=function(){},l.prototype.close=function(){this._clearFn.call(window,this._id)},e.setImmediate="function"==typeof t?t:function(t){var n=f++,u=!(arguments.length<2)&&i.call(arguments,1);return r[n]=!0,o((function(){r[n]&&(u?t.apply(null,u):t.call(null),e.clearImmediate(n))})),n},e.clearImmediate="function"==typeof n?n:function(t){delete r[t]}}).call(this,i({}).setImmediate,i({}).clearImmediate)},function(n){return e||t(e={exports:{},parent:n},e.exports),e.exports}),r={},u={},f=u={};function l(){throw new Error("setTimeout has not been defined")}function c(){throw new Error("clearTimeout has not been defined")}function a(t){if(n===setTimeout)return setTimeout(t,0);if((n===l||!n)&&setTimeout)return n=setTimeout,setTimeout(t,0);try{return n(t,0)}catch(e){try{return n.call(null,t,0)}catch(e){return n.call(this,t,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:l}catch(e){n=l}try{o="function"==typeof clearTimeout?clearTimeout:c}catch(e){o=c}}();var s,d=[],p=!1,h=-1;function y(){p&&s&&(p=!1,s.length?d=s.concat(d):h=-1,d.length&&m())}function m(){if(!p){var t=a(y);p=!0;for(var n=d.length;n;){for(s=d,d=[];++h<n;)s&&s[h].run();h=-1,n=d.length}s=null,p=!1,function(t){if(o===clearTimeout)return clearTimeout(t);if((o===c||!o)&&clearTimeout)return o=clearTimeout,clearTimeout(t);try{o(t)}catch(e){try{return o.call(null,t)}catch(e){return o.call(this,t)}}}(t)}}function w(t,e){this.fun=t,this.array=e}function g(){}f.nextTick=function(t){var e=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)e[n-1]=arguments[n];d.push(new w(t,e)),1!==d.length||p||a(m)},w.prototype.run=function(){this.fun.apply(null,this.array)},f.title="browser",f.browser=!0,f.env={},f.argv=[],f.version="",f.versions={},f.on=g,f.addListener=g,f.once=g,f.off=g,f.removeListener=g,f.removeAllListeners=g,f.emit=g,f.prependListener=g,f.prependOnceListener=g,f.listeners=function(t){return[]},f.binding=function(t){throw new Error("process.binding is not supported")},f.cwd=function(){return"/"},f.chdir=function(t){throw new Error("process.chdir is not supported")},f.umask=function(){return 0};var v={};return function(t){"use strict";function e(n,o){var i=this;r("object"==typeof n),r("number"==typeof o),i.id=++o,i.options=n;var u=[],f={lookup:{},history:[]},l=[],c={work_counter:0,gate:!1,running:!1,clear:null,firstclear:null,tm_in:null};function a(){if(c.running){i.isclear()||c.tm_in||(c.tm_in=setInterval(d,n.interval));do{var t=!1,e=null;c.gate||(e=u.shift()),e&&(r("object"==typeof e),r("string"==typeof e.id),r("function"==typeof e.fn),f.lookup[e.id]=e,f.history.push(e),c.gate=e.gate,e.start=Date.now(),e.callback=s(e),l.push(e),e.fn(e.callback),t=!0)}while(t)}}function s(e){return function(){if(!e.done){for(e.done=!0,delete f.lookup[e.id];f.history[0]&&f.history[0].done;)f.history.shift();for(;l[0]&&l[0].done;)l.shift();if(e.gate&&(c.gate=!1),0===u.length&&0===f.history.length){if(clearInterval(c.tm_in),c.tm_in=null,c.firstclear){var n=c.firstclear;c.firstclear=null,n()}c.clear&&c.clear()}t(a)}}}function d(){for(var t=Date.now(),e=null,n=0;n<l.length;++n)!(e=l[n]).gate&&!e.done&&e.tm<t-e.start&&(e.ontm&&e.ontm(e.tm,e.start,t),e.callback())}i.start=function(e){return r(null==e||"function"==typeof e),t((function(){c.running=!0,e&&(c.firstclear=e),a()})),i},i.pause=function(){c.running=!1},i.clear=function(t){return r("function"==typeof t),c.clear=t,i},i.isclear=function(){return 0===u.length&&0===f.history.length},i.add=function(e){return r("object"==typeof e),r("function"==typeof e.fn),r(null==e.id||"string"==typeof e.id),r(null==e.tm||"number"==typeof e.tm),r(null==e.dn||"string"==typeof e.dn),c.work_counter+=1,e.id=e.id||""+c.work_counter,e.ge=i.id,e.tm=null==e.tm?n.timeout:e.tm,e.dn=e.dn||e.fn.name||""+Date.now(),e.ctxt={},u.push(e),c.running&&t(a),i},i.gate=function(){var t=new e(n,o);return i.add({gate:t,fn:function(e){t.start(e)}}),t},i.state=function(){for(var t=[],e=0;e<f.history.length;++e){var n=f.history[e];n.done||t.push({s:"a",ge:n.ge,dn:n.dn,id:n.id})}for(var o=0;o<u.length;++o){var i=u[o];i.gate?t.push(i.gate.state()):t.push({s:"w",ge:i.ge,dn:i.dn,id:i.id})}return t.internal={qlen:u.length,hlen:f.history.length,klen:Object.keys(f.lookup).length,tlen:d.length},t}}r="function"==typeof r?r:function(){},v=function(t){return(t=t||{}).interval=null==t.interval?111:t.interval,t.timeout=null==t.timeout?2222:t.timeout,r("object"==typeof t),r("number"==typeof t.interval),r("number"==typeof t.timeout),r(0<t.interval),r(0<t.timeout),new e(t,0)}}.call(this,i({}).setImmediate),v}))}.call(this,void 0!==e?e:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{}),t=t.exports}))}.call(this,void 0!==e?e:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{}),t=t.exports}))}.call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{}),t=t.exports}));