@withjoy/limiter 0.1.3 → 0.1.4-test

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.
Files changed (35) hide show
  1. package/README.md +4 -0
  2. package/limitd-redis/LICENSE +21 -0
  3. package/limitd-redis/README.md +183 -0
  4. package/limitd-redis/docker-compose.yml +11 -0
  5. package/limitd-redis/index.js +2 -0
  6. package/limitd-redis/lib/cb.js +45 -0
  7. package/limitd-redis/lib/client.js +135 -0
  8. package/limitd-redis/lib/db.js +501 -0
  9. package/limitd-redis/lib/db_ping.js +106 -0
  10. package/limitd-redis/lib/put.lua +31 -0
  11. package/limitd-redis/lib/take.lua +48 -0
  12. package/limitd-redis/lib/utils.js +116 -0
  13. package/limitd-redis/lib/validation.js +64 -0
  14. package/limitd-redis/node_modules/lru-cache/LICENSE +15 -0
  15. package/limitd-redis/node_modules/lru-cache/README.md +158 -0
  16. package/limitd-redis/node_modules/lru-cache/index.js +468 -0
  17. package/limitd-redis/node_modules/lru-cache/package.json +74 -0
  18. package/limitd-redis/node_modules/ms/index.js +162 -0
  19. package/limitd-redis/node_modules/ms/license.md +21 -0
  20. package/limitd-redis/node_modules/ms/package.json +73 -0
  21. package/limitd-redis/node_modules/ms/readme.md +59 -0
  22. package/limitd-redis/node_modules/yallist/LICENSE +15 -0
  23. package/limitd-redis/node_modules/yallist/README.md +204 -0
  24. package/limitd-redis/node_modules/yallist/iterator.js +7 -0
  25. package/limitd-redis/node_modules/yallist/package.json +65 -0
  26. package/limitd-redis/node_modules/yallist/yallist.js +370 -0
  27. package/limitd-redis/opslevel.yml +6 -0
  28. package/limitd-redis/package-lock.json +3484 -0
  29. package/limitd-redis/package.json +31 -0
  30. package/limitd-redis/test/cb.tests.js +124 -0
  31. package/limitd-redis/test/client.tests.js +194 -0
  32. package/limitd-redis/test/db.tests.js +1318 -0
  33. package/limitd-redis/test/validation.tests.js +124 -0
  34. package/limiter.js +57 -1
  35. package/package.json +3 -2
@@ -0,0 +1,468 @@
1
+ 'use strict'
2
+
3
+ module.exports = LRUCache
4
+
5
+ // This will be a proper iterable 'Map' in engines that support it,
6
+ // or a fakey-fake PseudoMap in older versions.
7
+ var Map = require('pseudomap')
8
+ var util = require('util')
9
+
10
+ // A linked list to keep track of recently-used-ness
11
+ var Yallist = require('yallist')
12
+
13
+ // use symbols if possible, otherwise just _props
14
+ var hasSymbol = typeof Symbol === 'function' && process.env._nodeLRUCacheForceNoSymbol !== '1'
15
+ var makeSymbol
16
+ if (hasSymbol) {
17
+ makeSymbol = function (key) {
18
+ return Symbol(key)
19
+ }
20
+ } else {
21
+ makeSymbol = function (key) {
22
+ return '_' + key
23
+ }
24
+ }
25
+
26
+ var MAX = makeSymbol('max')
27
+ var LENGTH = makeSymbol('length')
28
+ var LENGTH_CALCULATOR = makeSymbol('lengthCalculator')
29
+ var ALLOW_STALE = makeSymbol('allowStale')
30
+ var MAX_AGE = makeSymbol('maxAge')
31
+ var DISPOSE = makeSymbol('dispose')
32
+ var NO_DISPOSE_ON_SET = makeSymbol('noDisposeOnSet')
33
+ var LRU_LIST = makeSymbol('lruList')
34
+ var CACHE = makeSymbol('cache')
35
+
36
+ function naiveLength () { return 1 }
37
+
38
+ // lruList is a yallist where the head is the youngest
39
+ // item, and the tail is the oldest. the list contains the Hit
40
+ // objects as the entries.
41
+ // Each Hit object has a reference to its Yallist.Node. This
42
+ // never changes.
43
+ //
44
+ // cache is a Map (or PseudoMap) that matches the keys to
45
+ // the Yallist.Node object.
46
+ function LRUCache (options) {
47
+ if (!(this instanceof LRUCache)) {
48
+ return new LRUCache(options)
49
+ }
50
+
51
+ if (typeof options === 'number') {
52
+ options = { max: options }
53
+ }
54
+
55
+ if (!options) {
56
+ options = {}
57
+ }
58
+
59
+ var max = this[MAX] = options.max
60
+ // Kind of weird to have a default max of Infinity, but oh well.
61
+ if (!max ||
62
+ !(typeof max === 'number') ||
63
+ max <= 0) {
64
+ this[MAX] = Infinity
65
+ }
66
+
67
+ var lc = options.length || naiveLength
68
+ if (typeof lc !== 'function') {
69
+ lc = naiveLength
70
+ }
71
+ this[LENGTH_CALCULATOR] = lc
72
+
73
+ this[ALLOW_STALE] = options.stale || false
74
+ this[MAX_AGE] = options.maxAge || 0
75
+ this[DISPOSE] = options.dispose
76
+ this[NO_DISPOSE_ON_SET] = options.noDisposeOnSet || false
77
+ this.reset()
78
+ }
79
+
80
+ // resize the cache when the max changes.
81
+ Object.defineProperty(LRUCache.prototype, 'max', {
82
+ set: function (mL) {
83
+ if (!mL || !(typeof mL === 'number') || mL <= 0) {
84
+ mL = Infinity
85
+ }
86
+ this[MAX] = mL
87
+ trim(this)
88
+ },
89
+ get: function () {
90
+ return this[MAX]
91
+ },
92
+ enumerable: true
93
+ })
94
+
95
+ Object.defineProperty(LRUCache.prototype, 'allowStale', {
96
+ set: function (allowStale) {
97
+ this[ALLOW_STALE] = !!allowStale
98
+ },
99
+ get: function () {
100
+ return this[ALLOW_STALE]
101
+ },
102
+ enumerable: true
103
+ })
104
+
105
+ Object.defineProperty(LRUCache.prototype, 'maxAge', {
106
+ set: function (mA) {
107
+ if (!mA || !(typeof mA === 'number') || mA < 0) {
108
+ mA = 0
109
+ }
110
+ this[MAX_AGE] = mA
111
+ trim(this)
112
+ },
113
+ get: function () {
114
+ return this[MAX_AGE]
115
+ },
116
+ enumerable: true
117
+ })
118
+
119
+ // resize the cache when the lengthCalculator changes.
120
+ Object.defineProperty(LRUCache.prototype, 'lengthCalculator', {
121
+ set: function (lC) {
122
+ if (typeof lC !== 'function') {
123
+ lC = naiveLength
124
+ }
125
+ if (lC !== this[LENGTH_CALCULATOR]) {
126
+ this[LENGTH_CALCULATOR] = lC
127
+ this[LENGTH] = 0
128
+ this[LRU_LIST].forEach(function (hit) {
129
+ hit.length = this[LENGTH_CALCULATOR](hit.value, hit.key)
130
+ this[LENGTH] += hit.length
131
+ }, this)
132
+ }
133
+ trim(this)
134
+ },
135
+ get: function () { return this[LENGTH_CALCULATOR] },
136
+ enumerable: true
137
+ })
138
+
139
+ Object.defineProperty(LRUCache.prototype, 'length', {
140
+ get: function () { return this[LENGTH] },
141
+ enumerable: true
142
+ })
143
+
144
+ Object.defineProperty(LRUCache.prototype, 'itemCount', {
145
+ get: function () { return this[LRU_LIST].length },
146
+ enumerable: true
147
+ })
148
+
149
+ LRUCache.prototype.rforEach = function (fn, thisp) {
150
+ thisp = thisp || this
151
+ for (var walker = this[LRU_LIST].tail; walker !== null;) {
152
+ var prev = walker.prev
153
+ forEachStep(this, fn, walker, thisp)
154
+ walker = prev
155
+ }
156
+ }
157
+
158
+ function forEachStep (self, fn, node, thisp) {
159
+ var hit = node.value
160
+ if (isStale(self, hit)) {
161
+ del(self, node)
162
+ if (!self[ALLOW_STALE]) {
163
+ hit = undefined
164
+ }
165
+ }
166
+ if (hit) {
167
+ fn.call(thisp, hit.value, hit.key, self)
168
+ }
169
+ }
170
+
171
+ LRUCache.prototype.forEach = function (fn, thisp) {
172
+ thisp = thisp || this
173
+ for (var walker = this[LRU_LIST].head; walker !== null;) {
174
+ var next = walker.next
175
+ forEachStep(this, fn, walker, thisp)
176
+ walker = next
177
+ }
178
+ }
179
+
180
+ LRUCache.prototype.keys = function () {
181
+ return this[LRU_LIST].toArray().map(function (k) {
182
+ return k.key
183
+ }, this)
184
+ }
185
+
186
+ LRUCache.prototype.values = function () {
187
+ return this[LRU_LIST].toArray().map(function (k) {
188
+ return k.value
189
+ }, this)
190
+ }
191
+
192
+ LRUCache.prototype.reset = function () {
193
+ if (this[DISPOSE] &&
194
+ this[LRU_LIST] &&
195
+ this[LRU_LIST].length) {
196
+ this[LRU_LIST].forEach(function (hit) {
197
+ this[DISPOSE](hit.key, hit.value)
198
+ }, this)
199
+ }
200
+
201
+ this[CACHE] = new Map() // hash of items by key
202
+ this[LRU_LIST] = new Yallist() // list of items in order of use recency
203
+ this[LENGTH] = 0 // length of items in the list
204
+ }
205
+
206
+ LRUCache.prototype.dump = function () {
207
+ return this[LRU_LIST].map(function (hit) {
208
+ if (!isStale(this, hit)) {
209
+ return {
210
+ k: hit.key,
211
+ v: hit.value,
212
+ e: hit.now + (hit.maxAge || 0)
213
+ }
214
+ }
215
+ }, this).toArray().filter(function (h) {
216
+ return h
217
+ })
218
+ }
219
+
220
+ LRUCache.prototype.dumpLru = function () {
221
+ return this[LRU_LIST]
222
+ }
223
+
224
+ /* istanbul ignore next */
225
+ LRUCache.prototype.inspect = function (n, opts) {
226
+ var str = 'LRUCache {'
227
+ var extras = false
228
+
229
+ var as = this[ALLOW_STALE]
230
+ if (as) {
231
+ str += '\n allowStale: true'
232
+ extras = true
233
+ }
234
+
235
+ var max = this[MAX]
236
+ if (max && max !== Infinity) {
237
+ if (extras) {
238
+ str += ','
239
+ }
240
+ str += '\n max: ' + util.inspect(max, opts)
241
+ extras = true
242
+ }
243
+
244
+ var maxAge = this[MAX_AGE]
245
+ if (maxAge) {
246
+ if (extras) {
247
+ str += ','
248
+ }
249
+ str += '\n maxAge: ' + util.inspect(maxAge, opts)
250
+ extras = true
251
+ }
252
+
253
+ var lc = this[LENGTH_CALCULATOR]
254
+ if (lc && lc !== naiveLength) {
255
+ if (extras) {
256
+ str += ','
257
+ }
258
+ str += '\n length: ' + util.inspect(this[LENGTH], opts)
259
+ extras = true
260
+ }
261
+
262
+ var didFirst = false
263
+ this[LRU_LIST].forEach(function (item) {
264
+ if (didFirst) {
265
+ str += ',\n '
266
+ } else {
267
+ if (extras) {
268
+ str += ',\n'
269
+ }
270
+ didFirst = true
271
+ str += '\n '
272
+ }
273
+ var key = util.inspect(item.key).split('\n').join('\n ')
274
+ var val = { value: item.value }
275
+ if (item.maxAge !== maxAge) {
276
+ val.maxAge = item.maxAge
277
+ }
278
+ if (lc !== naiveLength) {
279
+ val.length = item.length
280
+ }
281
+ if (isStale(this, item)) {
282
+ val.stale = true
283
+ }
284
+
285
+ val = util.inspect(val, opts).split('\n').join('\n ')
286
+ str += key + ' => ' + val
287
+ })
288
+
289
+ if (didFirst || extras) {
290
+ str += '\n'
291
+ }
292
+ str += '}'
293
+
294
+ return str
295
+ }
296
+
297
+ LRUCache.prototype.set = function (key, value, maxAge) {
298
+ maxAge = maxAge || this[MAX_AGE]
299
+
300
+ var now = maxAge ? Date.now() : 0
301
+ var len = this[LENGTH_CALCULATOR](value, key)
302
+
303
+ if (this[CACHE].has(key)) {
304
+ if (len > this[MAX]) {
305
+ del(this, this[CACHE].get(key))
306
+ return false
307
+ }
308
+
309
+ var node = this[CACHE].get(key)
310
+ var item = node.value
311
+
312
+ // dispose of the old one before overwriting
313
+ // split out into 2 ifs for better coverage tracking
314
+ if (this[DISPOSE]) {
315
+ if (!this[NO_DISPOSE_ON_SET]) {
316
+ this[DISPOSE](key, item.value)
317
+ }
318
+ }
319
+
320
+ item.now = now
321
+ item.maxAge = maxAge
322
+ item.value = value
323
+ this[LENGTH] += len - item.length
324
+ item.length = len
325
+ this.get(key)
326
+ trim(this)
327
+ return true
328
+ }
329
+
330
+ var hit = new Entry(key, value, len, now, maxAge)
331
+
332
+ // oversized objects fall out of cache automatically.
333
+ if (hit.length > this[MAX]) {
334
+ if (this[DISPOSE]) {
335
+ this[DISPOSE](key, value)
336
+ }
337
+ return false
338
+ }
339
+
340
+ this[LENGTH] += hit.length
341
+ this[LRU_LIST].unshift(hit)
342
+ this[CACHE].set(key, this[LRU_LIST].head)
343
+ trim(this)
344
+ return true
345
+ }
346
+
347
+ LRUCache.prototype.has = function (key) {
348
+ if (!this[CACHE].has(key)) return false
349
+ var hit = this[CACHE].get(key).value
350
+ if (isStale(this, hit)) {
351
+ return false
352
+ }
353
+ return true
354
+ }
355
+
356
+ LRUCache.prototype.get = function (key) {
357
+ return get(this, key, true)
358
+ }
359
+
360
+ LRUCache.prototype.peek = function (key) {
361
+ return get(this, key, false)
362
+ }
363
+
364
+ LRUCache.prototype.pop = function () {
365
+ var node = this[LRU_LIST].tail
366
+ if (!node) return null
367
+ del(this, node)
368
+ return node.value
369
+ }
370
+
371
+ LRUCache.prototype.del = function (key) {
372
+ del(this, this[CACHE].get(key))
373
+ }
374
+
375
+ LRUCache.prototype.load = function (arr) {
376
+ // reset the cache
377
+ this.reset()
378
+
379
+ var now = Date.now()
380
+ // A previous serialized cache has the most recent items first
381
+ for (var l = arr.length - 1; l >= 0; l--) {
382
+ var hit = arr[l]
383
+ var expiresAt = hit.e || 0
384
+ if (expiresAt === 0) {
385
+ // the item was created without expiration in a non aged cache
386
+ this.set(hit.k, hit.v)
387
+ } else {
388
+ var maxAge = expiresAt - now
389
+ // dont add already expired items
390
+ if (maxAge > 0) {
391
+ this.set(hit.k, hit.v, maxAge)
392
+ }
393
+ }
394
+ }
395
+ }
396
+
397
+ LRUCache.prototype.prune = function () {
398
+ var self = this
399
+ this[CACHE].forEach(function (value, key) {
400
+ get(self, key, false)
401
+ })
402
+ }
403
+
404
+ function get (self, key, doUse) {
405
+ var node = self[CACHE].get(key)
406
+ if (node) {
407
+ var hit = node.value
408
+ if (isStale(self, hit)) {
409
+ del(self, node)
410
+ if (!self[ALLOW_STALE]) hit = undefined
411
+ } else {
412
+ if (doUse) {
413
+ self[LRU_LIST].unshiftNode(node)
414
+ }
415
+ }
416
+ if (hit) hit = hit.value
417
+ }
418
+ return hit
419
+ }
420
+
421
+ function isStale (self, hit) {
422
+ if (!hit || (!hit.maxAge && !self[MAX_AGE])) {
423
+ return false
424
+ }
425
+ var stale = false
426
+ var diff = Date.now() - hit.now
427
+ if (hit.maxAge) {
428
+ stale = diff > hit.maxAge
429
+ } else {
430
+ stale = self[MAX_AGE] && (diff > self[MAX_AGE])
431
+ }
432
+ return stale
433
+ }
434
+
435
+ function trim (self) {
436
+ if (self[LENGTH] > self[MAX]) {
437
+ for (var walker = self[LRU_LIST].tail;
438
+ self[LENGTH] > self[MAX] && walker !== null;) {
439
+ // We know that we're about to delete this one, and also
440
+ // what the next least recently used key will be, so just
441
+ // go ahead and set it now.
442
+ var prev = walker.prev
443
+ del(self, walker)
444
+ walker = prev
445
+ }
446
+ }
447
+ }
448
+
449
+ function del (self, node) {
450
+ if (node) {
451
+ var hit = node.value
452
+ if (self[DISPOSE]) {
453
+ self[DISPOSE](hit.key, hit.value)
454
+ }
455
+ self[LENGTH] -= hit.length
456
+ self[CACHE].delete(hit.key)
457
+ self[LRU_LIST].removeNode(node)
458
+ }
459
+ }
460
+
461
+ // classy, since V8 prefers predictable objects.
462
+ function Entry (key, value, length, now, maxAge) {
463
+ this.key = key
464
+ this.value = value
465
+ this.length = length
466
+ this.now = now
467
+ this.maxAge = maxAge || 0
468
+ }
@@ -0,0 +1,74 @@
1
+ {
2
+ "_args": [
3
+ [
4
+ "lru-cache@4.1.5",
5
+ "/Users/mindpath/work/joy/limiter"
6
+ ]
7
+ ],
8
+ "_from": "lru-cache@4.1.5",
9
+ "_id": "lru-cache@4.1.5",
10
+ "_inBundle": false,
11
+ "_integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
12
+ "_location": "/limitd-redis/lru-cache",
13
+ "_phantomChildren": {},
14
+ "_requested": {
15
+ "type": "version",
16
+ "registry": true,
17
+ "raw": "lru-cache@4.1.5",
18
+ "name": "lru-cache",
19
+ "escapedName": "lru-cache",
20
+ "rawSpec": "4.1.5",
21
+ "saveSpec": null,
22
+ "fetchSpec": "4.1.5"
23
+ },
24
+ "_requiredBy": [
25
+ "/limitd-redis"
26
+ ],
27
+ "_resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
28
+ "_spec": "4.1.5",
29
+ "_where": "/Users/mindpath/work/joy/limiter",
30
+ "author": {
31
+ "name": "Isaac Z. Schlueter",
32
+ "email": "i@izs.me"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/isaacs/node-lru-cache/issues"
36
+ },
37
+ "dependencies": {
38
+ "pseudomap": "^1.0.2",
39
+ "yallist": "^2.1.2"
40
+ },
41
+ "description": "A cache object that deletes the least-recently-used items.",
42
+ "devDependencies": {
43
+ "benchmark": "^2.1.4",
44
+ "standard": "^12.0.1",
45
+ "tap": "^12.1.0"
46
+ },
47
+ "files": [
48
+ "index.js"
49
+ ],
50
+ "homepage": "https://github.com/isaacs/node-lru-cache#readme",
51
+ "keywords": [
52
+ "mru",
53
+ "lru",
54
+ "cache"
55
+ ],
56
+ "license": "ISC",
57
+ "main": "index.js",
58
+ "name": "lru-cache",
59
+ "repository": {
60
+ "type": "git",
61
+ "url": "git://github.com/isaacs/node-lru-cache.git"
62
+ },
63
+ "scripts": {
64
+ "coveragerport": "tap --coverage-report=html",
65
+ "lintfix": "standard --fix test/*.js index.js",
66
+ "postpublish": "git push origin --all; git push origin --tags",
67
+ "posttest": "standard test/*.js index.js",
68
+ "postversion": "npm publish --tag=legacy",
69
+ "preversion": "npm test",
70
+ "snap": "TAP_SNAPSHOT=1 tap test/*.js -J",
71
+ "test": "tap test/*.js --100 -J"
72
+ },
73
+ "version": "4.1.5"
74
+ }
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Helpers.
3
+ */
4
+
5
+ var s = 1000;
6
+ var m = s * 60;
7
+ var h = m * 60;
8
+ var d = h * 24;
9
+ var w = d * 7;
10
+ var y = d * 365.25;
11
+
12
+ /**
13
+ * Parse or format the given `val`.
14
+ *
15
+ * Options:
16
+ *
17
+ * - `long` verbose formatting [false]
18
+ *
19
+ * @param {String|Number} val
20
+ * @param {Object} [options]
21
+ * @throws {Error} throw an error if val is not a non-empty string or a number
22
+ * @return {String|Number}
23
+ * @api public
24
+ */
25
+
26
+ module.exports = function (val, options) {
27
+ options = options || {};
28
+ var type = typeof val;
29
+ if (type === 'string' && val.length > 0) {
30
+ return parse(val);
31
+ } else if (type === 'number' && isFinite(val)) {
32
+ return options.long ? fmtLong(val) : fmtShort(val);
33
+ }
34
+ throw new Error(
35
+ 'val is not a non-empty string or a valid number. val=' +
36
+ JSON.stringify(val)
37
+ );
38
+ };
39
+
40
+ /**
41
+ * Parse the given `str` and return milliseconds.
42
+ *
43
+ * @param {String} str
44
+ * @return {Number}
45
+ * @api private
46
+ */
47
+
48
+ function parse(str) {
49
+ str = String(str);
50
+ if (str.length > 100) {
51
+ return;
52
+ }
53
+ var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(
54
+ str
55
+ );
56
+ if (!match) {
57
+ return;
58
+ }
59
+ var n = parseFloat(match[1]);
60
+ var type = (match[2] || 'ms').toLowerCase();
61
+ switch (type) {
62
+ case 'years':
63
+ case 'year':
64
+ case 'yrs':
65
+ case 'yr':
66
+ case 'y':
67
+ return n * y;
68
+ case 'weeks':
69
+ case 'week':
70
+ case 'w':
71
+ return n * w;
72
+ case 'days':
73
+ case 'day':
74
+ case 'd':
75
+ return n * d;
76
+ case 'hours':
77
+ case 'hour':
78
+ case 'hrs':
79
+ case 'hr':
80
+ case 'h':
81
+ return n * h;
82
+ case 'minutes':
83
+ case 'minute':
84
+ case 'mins':
85
+ case 'min':
86
+ case 'm':
87
+ return n * m;
88
+ case 'seconds':
89
+ case 'second':
90
+ case 'secs':
91
+ case 'sec':
92
+ case 's':
93
+ return n * s;
94
+ case 'milliseconds':
95
+ case 'millisecond':
96
+ case 'msecs':
97
+ case 'msec':
98
+ case 'ms':
99
+ return n;
100
+ default:
101
+ return undefined;
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Short format for `ms`.
107
+ *
108
+ * @param {Number} ms
109
+ * @return {String}
110
+ * @api private
111
+ */
112
+
113
+ function fmtShort(ms) {
114
+ var msAbs = Math.abs(ms);
115
+ if (msAbs >= d) {
116
+ return Math.round(ms / d) + 'd';
117
+ }
118
+ if (msAbs >= h) {
119
+ return Math.round(ms / h) + 'h';
120
+ }
121
+ if (msAbs >= m) {
122
+ return Math.round(ms / m) + 'm';
123
+ }
124
+ if (msAbs >= s) {
125
+ return Math.round(ms / s) + 's';
126
+ }
127
+ return ms + 'ms';
128
+ }
129
+
130
+ /**
131
+ * Long format for `ms`.
132
+ *
133
+ * @param {Number} ms
134
+ * @return {String}
135
+ * @api private
136
+ */
137
+
138
+ function fmtLong(ms) {
139
+ var msAbs = Math.abs(ms);
140
+ if (msAbs >= d) {
141
+ return plural(ms, msAbs, d, 'day');
142
+ }
143
+ if (msAbs >= h) {
144
+ return plural(ms, msAbs, h, 'hour');
145
+ }
146
+ if (msAbs >= m) {
147
+ return plural(ms, msAbs, m, 'minute');
148
+ }
149
+ if (msAbs >= s) {
150
+ return plural(ms, msAbs, s, 'second');
151
+ }
152
+ return ms + ' ms';
153
+ }
154
+
155
+ /**
156
+ * Pluralization helper.
157
+ */
158
+
159
+ function plural(ms, msAbs, n, name) {
160
+ var isPlural = msAbs >= n * 1.5;
161
+ return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : '');
162
+ }
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Vercel, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.