ultimate-express 1.3.8 → 1.3.10
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.
- package/package.json +2 -1
- package/src/application.js +2 -2
- package/src/declarative.js +25 -1
- package/src/request.js +11 -14
- package/src/response.js +9 -5
- package/src/router.js +65 -52
- package/src/utils.js +37 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultimate-express",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.10",
|
|
4
4
|
"description": "The Ultimate Express. Fastest http server with full Express compatibility, based on uWebSockets.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"vary": "^1.1.2"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
+
"@codechecks/client": "^0.1.12",
|
|
66
67
|
"body-parser": "^1.20.3",
|
|
67
68
|
"compression": "^1.7.4",
|
|
68
69
|
"cookie-parser": "^1.4.6",
|
package/src/application.js
CHANGED
|
@@ -63,7 +63,7 @@ class Application extends Router {
|
|
|
63
63
|
this.ssl = false;
|
|
64
64
|
}
|
|
65
65
|
this.cache = new NullObject();
|
|
66
|
-
this.engines =
|
|
66
|
+
this.engines = {};
|
|
67
67
|
this.locals = {
|
|
68
68
|
settings: this.settings
|
|
69
69
|
};
|
|
@@ -292,7 +292,7 @@ class Application extends Router {
|
|
|
292
292
|
view = new View(name, {
|
|
293
293
|
defaultEngine: this.get('view engine'),
|
|
294
294
|
root: this.get('views'),
|
|
295
|
-
engines: this.engines
|
|
295
|
+
engines: {...this.engines}
|
|
296
296
|
});
|
|
297
297
|
if(!view.path) {
|
|
298
298
|
const dirs = Array.isArray(view.root) && view.root.length > 1
|
package/src/declarative.js
CHANGED
|
@@ -6,6 +6,11 @@ const parser = acorn.Parser;
|
|
|
6
6
|
|
|
7
7
|
const allowedResMethods = ['set', 'header', 'setHeader', 'status', 'send', 'end', 'append'];
|
|
8
8
|
const allowedIdentifiers = ['query', 'params', ...allowedResMethods];
|
|
9
|
+
const objKeyRegex = /[\s{\n]([A-Za-z-0-9_]+)(\s|\n)*?:/g;
|
|
10
|
+
|
|
11
|
+
function replaceSingleCharacter(str, index, char) {
|
|
12
|
+
return str.slice(0, index) + char + str.slice(index + 1);
|
|
13
|
+
}
|
|
9
14
|
|
|
10
15
|
// generates a declarative response from a callback
|
|
11
16
|
// uWS allows creating such responses and they are extremely fast
|
|
@@ -274,11 +279,19 @@ module.exports = function compileDeclarative(cb, app) {
|
|
|
274
279
|
}
|
|
275
280
|
body.push(...stuff.reverse());
|
|
276
281
|
} else if(arg.type === 'ObjectExpression') {
|
|
282
|
+
if(call.obj.propertyName === 'end') {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
277
285
|
// only simple objects can be optimized
|
|
286
|
+
let objCode = code;
|
|
278
287
|
for(let property of arg.properties) {
|
|
279
288
|
if(property.key.type !== 'Identifier' && property.key.type !== 'Literal') {
|
|
280
289
|
return false;
|
|
281
290
|
}
|
|
291
|
+
if(property.value.raw.startsWith("'") && property.value.raw.endsWith("'") && !property.value.value.includes("'")) {
|
|
292
|
+
objCode = replaceSingleCharacter(objCode, property.value.start, '"');
|
|
293
|
+
objCode = replaceSingleCharacter(objCode, property.value.end - 1, '"');
|
|
294
|
+
}
|
|
282
295
|
if(property.value.type !== 'Literal') {
|
|
283
296
|
return false;
|
|
284
297
|
}
|
|
@@ -286,7 +299,18 @@ module.exports = function compileDeclarative(cb, app) {
|
|
|
286
299
|
if(typeof app.get('json replacer') !== 'undefined' && typeof app.get('json replacer') !== 'string') {
|
|
287
300
|
return false;
|
|
288
301
|
}
|
|
289
|
-
|
|
302
|
+
|
|
303
|
+
headers.push(['content-type', 'application/json; charset=utf-8']);
|
|
304
|
+
body.push({
|
|
305
|
+
type: 'text',
|
|
306
|
+
value:
|
|
307
|
+
stringify(
|
|
308
|
+
JSON.parse(objCode.slice(arg.start, arg.end).replace(objKeyRegex, '"$1":')),
|
|
309
|
+
app.get('json replacer'),
|
|
310
|
+
app.get('json spaces'),
|
|
311
|
+
app.get('json escape')
|
|
312
|
+
)
|
|
313
|
+
});
|
|
290
314
|
} else {
|
|
291
315
|
return false;
|
|
292
316
|
}
|
package/src/request.js
CHANGED
|
@@ -45,6 +45,7 @@ module.exports = class Request extends Readable {
|
|
|
45
45
|
this._req.forEach((key, value) => {
|
|
46
46
|
this.#rawHeadersEntries.push([key, value]);
|
|
47
47
|
});
|
|
48
|
+
this.routeCount = 0;
|
|
48
49
|
this.key = key++;
|
|
49
50
|
if(key > 100000) {
|
|
50
51
|
key = 0;
|
|
@@ -65,7 +66,7 @@ module.exports = class Request extends Readable {
|
|
|
65
66
|
this._opPath = this._opPath.slice(0, -1);
|
|
66
67
|
}
|
|
67
68
|
this.method = req.getCaseSensitiveMethod().toUpperCase();
|
|
68
|
-
this.params =
|
|
69
|
+
this.params = {};
|
|
69
70
|
|
|
70
71
|
this._gotParams = new Set();
|
|
71
72
|
this._stack = [];
|
|
@@ -209,7 +210,7 @@ module.exports = class Request extends Readable {
|
|
|
209
210
|
} else {
|
|
210
211
|
this.#cachedQuery = new NullObject();
|
|
211
212
|
}
|
|
212
|
-
return this.#cachedQuery;
|
|
213
|
+
return {...this.#cachedQuery};
|
|
213
214
|
}
|
|
214
215
|
|
|
215
216
|
get secure() {
|
|
@@ -307,23 +308,19 @@ module.exports = class Request extends Readable {
|
|
|
307
308
|
}
|
|
308
309
|
|
|
309
310
|
accepts(...types) {
|
|
310
|
-
|
|
311
|
-
return accept.types(...types);
|
|
311
|
+
return accepts(this).types(...types);
|
|
312
312
|
}
|
|
313
313
|
|
|
314
314
|
acceptsCharsets(...charsets) {
|
|
315
|
-
|
|
316
|
-
return accept.charsets(...charsets);
|
|
315
|
+
return accepts(this).charsets(...charsets);
|
|
317
316
|
}
|
|
318
317
|
|
|
319
318
|
acceptsEncodings(...encodings) {
|
|
320
|
-
|
|
321
|
-
return accept.encodings(...encodings);
|
|
319
|
+
return accepts(this).encodings(...encodings);
|
|
322
320
|
}
|
|
323
321
|
|
|
324
322
|
acceptsLanguages(...languages) {
|
|
325
|
-
|
|
326
|
-
return accept.languages(...languages);
|
|
323
|
+
return accepts(this).languages(...languages);
|
|
327
324
|
}
|
|
328
325
|
|
|
329
326
|
is(type) {
|
|
@@ -350,7 +347,7 @@ module.exports = class Request extends Readable {
|
|
|
350
347
|
get headers() {
|
|
351
348
|
// https://nodejs.org/api/http.html#messageheaders
|
|
352
349
|
if(this.#cachedHeaders) {
|
|
353
|
-
return this.#cachedHeaders;
|
|
350
|
+
return {...this.#cachedHeaders};
|
|
354
351
|
}
|
|
355
352
|
this.#cachedHeaders = new NullObject();
|
|
356
353
|
for (let index = 0, len = this.#rawHeadersEntries.length; index < len; index++) {
|
|
@@ -375,7 +372,7 @@ module.exports = class Request extends Readable {
|
|
|
375
372
|
this.#cachedHeaders[key] = value;
|
|
376
373
|
}
|
|
377
374
|
}
|
|
378
|
-
return this.#cachedHeaders;
|
|
375
|
+
return {...this.#cachedHeaders};
|
|
379
376
|
}
|
|
380
377
|
|
|
381
378
|
get headersDistinct() {
|
|
@@ -391,7 +388,7 @@ module.exports = class Request extends Readable {
|
|
|
391
388
|
}
|
|
392
389
|
this.#cachedDistinctHeaders[key].push(value);
|
|
393
390
|
});
|
|
394
|
-
return this.#cachedDistinctHeaders;
|
|
391
|
+
return {...this.#cachedDistinctHeaders};
|
|
395
392
|
}
|
|
396
393
|
|
|
397
394
|
get rawHeaders() {
|
|
@@ -402,4 +399,4 @@ module.exports = class Request extends Readable {
|
|
|
402
399
|
}
|
|
403
400
|
return res;
|
|
404
401
|
}
|
|
405
|
-
}
|
|
402
|
+
}
|
package/src/response.js
CHANGED
|
@@ -83,6 +83,7 @@ module.exports = class Response extends Writable {
|
|
|
83
83
|
if(this.app.get('x-powered-by')) {
|
|
84
84
|
this.headers['x-powered-by'] = 'UltimateExpress';
|
|
85
85
|
}
|
|
86
|
+
|
|
86
87
|
// support for node internal
|
|
87
88
|
this[kOutHeaders] = new Proxy(this.headers, {
|
|
88
89
|
set: (obj, prop, value) => {
|
|
@@ -256,8 +257,10 @@ module.exports = class Response extends Writable {
|
|
|
256
257
|
if(this.socketExists) this.socket.emit('close');
|
|
257
258
|
});
|
|
258
259
|
|
|
260
|
+
this.emit('finish')
|
|
259
261
|
return this;
|
|
260
262
|
}
|
|
263
|
+
|
|
261
264
|
send(body) {
|
|
262
265
|
if(this.headersSent) {
|
|
263
266
|
throw new Error('Can\'t write body: Response was already sent');
|
|
@@ -288,6 +291,7 @@ module.exports = class Response extends Writable {
|
|
|
288
291
|
}
|
|
289
292
|
return this.end(body);
|
|
290
293
|
}
|
|
294
|
+
|
|
291
295
|
sendFile(path, options = new NullObject(), callback) {
|
|
292
296
|
if(typeof path !== 'string') {
|
|
293
297
|
throw new TypeError('path argument is required to res.sendFile');
|
|
@@ -496,10 +500,10 @@ module.exports = class Response extends Writable {
|
|
|
496
500
|
if (typeof filename === 'function') {
|
|
497
501
|
done = filename;
|
|
498
502
|
name = null;
|
|
499
|
-
opts =
|
|
503
|
+
opts = {};
|
|
500
504
|
} else if (typeof options === 'function') {
|
|
501
505
|
done = options;
|
|
502
|
-
opts =
|
|
506
|
+
opts = {};
|
|
503
507
|
}
|
|
504
508
|
|
|
505
509
|
// support optional filename, where options may be in it's place
|
|
@@ -577,10 +581,10 @@ module.exports = class Response extends Writable {
|
|
|
577
581
|
render(view, options, callback) {
|
|
578
582
|
if(typeof options === 'function') {
|
|
579
583
|
callback = options;
|
|
580
|
-
options =
|
|
584
|
+
options = {};
|
|
581
585
|
}
|
|
582
586
|
if(!options) {
|
|
583
|
-
options =
|
|
587
|
+
options = {};
|
|
584
588
|
} else {
|
|
585
589
|
options = Object.assign({}, options);
|
|
586
590
|
}
|
|
@@ -594,7 +598,7 @@ module.exports = class Response extends Writable {
|
|
|
594
598
|
}
|
|
595
599
|
cookie(name, value, options) {
|
|
596
600
|
if(!options) {
|
|
597
|
-
options =
|
|
601
|
+
options = {};
|
|
598
602
|
}
|
|
599
603
|
let val = typeof value === 'object' ? "j:"+JSON.stringify(value) : String(value);
|
|
600
604
|
if(options.maxAge != null) {
|
package/src/router.js
CHANGED
|
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
const { patternToRegex, needsConversionToRegex, deprecated, findIndexStartingFrom, canBeOptimized, NullObject } = require("./utils.js");
|
|
17
|
+
const { patternToRegex, needsConversionToRegex, deprecated, findIndexStartingFrom, canBeOptimized, NullObject, EMPTY_REGEX } = require("./utils.js");
|
|
18
18
|
const Response = require("./response.js");
|
|
19
19
|
const Request = require("./request.js");
|
|
20
20
|
const { EventEmitter } = require("tseep");
|
|
@@ -93,6 +93,9 @@ module.exports = class Router extends EventEmitter {
|
|
|
93
93
|
|
|
94
94
|
getFullMountpath(req) {
|
|
95
95
|
let fullStack = req._stack.join("");
|
|
96
|
+
if(!fullStack){
|
|
97
|
+
return EMPTY_REGEX;
|
|
98
|
+
}
|
|
96
99
|
let fullMountpath = this._mountpathCache.get(fullStack);
|
|
97
100
|
if(!fullMountpath) {
|
|
98
101
|
fullMountpath = patternToRegex(fullStack, true);
|
|
@@ -118,7 +121,9 @@ module.exports = class Router extends EventEmitter {
|
|
|
118
121
|
}
|
|
119
122
|
return pattern === path;
|
|
120
123
|
}
|
|
121
|
-
|
|
124
|
+
if (pattern === EMPTY_REGEX){
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
122
127
|
return pattern.test(path);
|
|
123
128
|
}
|
|
124
129
|
|
|
@@ -355,60 +360,60 @@ module.exports = class Router extends EventEmitter {
|
|
|
355
360
|
}
|
|
356
361
|
|
|
357
362
|
_preprocessRequest(req, res, route) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
}
|
|
363
|
+
req.route = route;
|
|
364
|
+
if(route.optimizedParams) {
|
|
365
|
+
req.params = {...req.optimizedParams};
|
|
366
|
+
} else if(typeof route.path === 'string' && (route.path.includes(':') || route.path.includes('*')) && route.pattern instanceof RegExp) {
|
|
367
|
+
let path = req._originalPath;
|
|
368
|
+
if(req._stack.length > 0) {
|
|
369
|
+
path = path.replace(this.getFullMountpath(req), '');
|
|
370
|
+
}
|
|
371
|
+
req.params = {...this._extractParams(route.pattern, path)};
|
|
372
|
+
if(req._paramStack.length > 0) {
|
|
373
|
+
for(let params of req._paramStack) {
|
|
374
|
+
req.params = {...params, ...req.params};
|
|
371
375
|
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
376
|
+
}
|
|
377
|
+
} else {
|
|
378
|
+
req.params = {};
|
|
379
|
+
if(req._paramStack.length > 0) {
|
|
380
|
+
for(let params of req._paramStack) {
|
|
381
|
+
req.params = {...params, ...req.params};
|
|
378
382
|
}
|
|
379
383
|
}
|
|
384
|
+
}
|
|
380
385
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}
|
|
386
|
+
if(this._paramCallbacks.size > 0) {
|
|
387
|
+
return new Promise(async resolve => {
|
|
388
|
+
for(let param in req.params) {
|
|
389
|
+
const pcs = this._paramCallbacks.get(param);
|
|
390
|
+
if(pcs && !req._gotParams.has(param)) {
|
|
391
|
+
req._gotParams.add(param);
|
|
392
|
+
for(let i = 0, len = pcs.length; i < len; i++) {
|
|
393
|
+
const fn = pcs[i];
|
|
394
|
+
await new Promise(resolveRoute => {
|
|
395
|
+
const next = (thingamabob) => {
|
|
396
|
+
if(thingamabob) {
|
|
397
|
+
if(thingamabob === 'route') {
|
|
398
|
+
return resolve('route');
|
|
399
|
+
} else {
|
|
400
|
+
this._handleError(thingamabob, req, res);
|
|
401
|
+
return resolve(false);
|
|
398
402
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
}
|
|
403
|
+
}
|
|
404
|
+
return resolveRoute();
|
|
405
|
+
};
|
|
406
|
+
req.next = next;
|
|
407
|
+
fn(req, res, next, req.params[param], param);
|
|
408
|
+
});
|
|
405
409
|
}
|
|
406
410
|
}
|
|
411
|
+
}
|
|
407
412
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
413
|
+
resolve(true)
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
return true;
|
|
412
417
|
}
|
|
413
418
|
|
|
414
419
|
param(name, fn) {
|
|
@@ -445,11 +450,18 @@ module.exports = class Router extends EventEmitter {
|
|
|
445
450
|
return this._routeRequest(req, res, 0, this._routes, false, skipUntil);
|
|
446
451
|
}
|
|
447
452
|
let callbackindex = 0;
|
|
448
|
-
|
|
453
|
+
|
|
454
|
+
// avoid calling _preprocessRequest as async function as its slower
|
|
455
|
+
// but it seems like calling it as async has unintended consequence of resetting max call stack size
|
|
456
|
+
// so call it as async when the request has been through every 300 routes to reset it
|
|
457
|
+
const continueRoute = this._paramCallbacks.size === 0 && req.routeCount % 300 !== 0 ?
|
|
458
|
+
this._preprocessRequest(req, res, route) : await this._preprocessRequest(req, res, route);
|
|
459
|
+
|
|
460
|
+
const strictRouting = this.get('strict routing');
|
|
449
461
|
if(route.use) {
|
|
450
|
-
const strictRouting = this.get('strict routing');
|
|
451
462
|
req._stack.push(route.path);
|
|
452
|
-
|
|
463
|
+
const fullMountpath = this.getFullMountpath(req);
|
|
464
|
+
req._opPath = fullMountpath !== EMPTY_REGEX ? req._originalPath.replace(fullMountpath, '') : req._originalPath;
|
|
453
465
|
if(req.endsWithSlash && req._opPath[req._opPath.length - 1] !== '/') {
|
|
454
466
|
if(strictRouting) {
|
|
455
467
|
req._opPath += '/';
|
|
@@ -470,7 +482,7 @@ module.exports = class Router extends EventEmitter {
|
|
|
470
482
|
if(thingamabob === 'route' || thingamabob === 'skipPop') {
|
|
471
483
|
if(route.use && thingamabob !== 'skipPop') {
|
|
472
484
|
req._stack.pop();
|
|
473
|
-
|
|
485
|
+
|
|
474
486
|
req._opPath = req._stack.length > 0 ? req._originalPath.replace(this.getFullMountpath(req), '') : req._originalPath;
|
|
475
487
|
if(strictRouting) {
|
|
476
488
|
if(req.endsWithSlash && req._opPath[req._opPath.length - 1] !== '/') {
|
|
@@ -490,6 +502,7 @@ module.exports = class Router extends EventEmitter {
|
|
|
490
502
|
req.app = req.app.parent;
|
|
491
503
|
}
|
|
492
504
|
}
|
|
505
|
+
req.routeCount++;
|
|
493
506
|
return resolve(this._routeRequest(req, res, routeIndex + 1, routes, skipCheck, skipUntil));
|
|
494
507
|
} else {
|
|
495
508
|
this._handleError(thingamabob, req, res);
|
package/src/utils.js
CHANGED
|
@@ -22,6 +22,8 @@ const querystring = require("fast-querystring");
|
|
|
22
22
|
const etag = require("etag");
|
|
23
23
|
const { Stats } = require("fs");
|
|
24
24
|
|
|
25
|
+
const EMPTY_REGEX = new RegExp(``);
|
|
26
|
+
|
|
25
27
|
function fastQueryParse(query, options) {
|
|
26
28
|
const len = query.length;
|
|
27
29
|
if(len === 0){
|
|
@@ -29,10 +31,12 @@ function fastQueryParse(query, options) {
|
|
|
29
31
|
}
|
|
30
32
|
if(len <= 128) {
|
|
31
33
|
if(!query.includes('[') && !query.includes('%5B') && !query.includes('.') && !query.includes('%2E')) {
|
|
32
|
-
|
|
34
|
+
// [Object: null prototype] issue
|
|
35
|
+
return {...querystring.parse(query)};
|
|
33
36
|
}
|
|
34
37
|
}
|
|
35
|
-
|
|
38
|
+
// [Object: null prototype] issue
|
|
39
|
+
return {...qs.parse(query, options)};
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
function removeDuplicateSlashes(path) {
|
|
@@ -44,7 +48,7 @@ function patternToRegex(pattern, isPrefix = false) {
|
|
|
44
48
|
return pattern;
|
|
45
49
|
}
|
|
46
50
|
if(isPrefix && pattern === '') {
|
|
47
|
-
return
|
|
51
|
+
return EMPTY_REGEX;
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
let regexPattern = pattern
|
|
@@ -102,17 +106,33 @@ function canBeOptimized(pattern) {
|
|
|
102
106
|
}
|
|
103
107
|
|
|
104
108
|
function acceptParams(str) {
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
109
|
+
const length = str.length;
|
|
110
|
+
const colonIndex = str.indexOf(';');
|
|
111
|
+
const index = colonIndex === -1 ? length : colonIndex;
|
|
112
|
+
const ret = { value: str.slice(0, index).trim(), quality: 1, params: {} };
|
|
113
|
+
|
|
114
|
+
while (index < length) {
|
|
115
|
+
const splitIndex = str.indexOf('=', index);
|
|
116
|
+
if (splitIndex === -1) break;
|
|
117
|
+
|
|
118
|
+
const colonIndex = str.indexOf(';', index);
|
|
119
|
+
const endIndex = colonIndex === -1 ? length : colonIndex;
|
|
120
|
+
|
|
121
|
+
if (splitIndex > endIndex) {
|
|
122
|
+
index = str.lastIndexOf(';', splitIndex - 1) + 1;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const key = str.slice(index, splitIndex).trim();
|
|
127
|
+
const value = str.slice(splitIndex + 1, endIndex).trim();
|
|
128
|
+
|
|
129
|
+
if (key === 'q') {
|
|
130
|
+
ret.quality = parseFloat(value);
|
|
131
|
+
} else {
|
|
132
|
+
ret.params[key] = value;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
index = endIndex + 1;
|
|
116
136
|
}
|
|
117
137
|
|
|
118
138
|
return ret;
|
|
@@ -205,7 +225,7 @@ function deprecated(oldMethod, newMethod, full = false) {
|
|
|
205
225
|
}
|
|
206
226
|
|
|
207
227
|
function findIndexStartingFrom(arr, fn, index = 0) {
|
|
208
|
-
for(let i = index
|
|
228
|
+
for(let i = index, end = arr.length; i < end; i++) {
|
|
209
229
|
if(fn(arr[i], i, arr)) {
|
|
210
230
|
return i;
|
|
211
231
|
}
|
|
@@ -346,5 +366,6 @@ module.exports = {
|
|
|
346
366
|
isRangeFresh,
|
|
347
367
|
findIndexStartingFrom,
|
|
348
368
|
fastQueryParse,
|
|
349
|
-
canBeOptimized
|
|
369
|
+
canBeOptimized,
|
|
370
|
+
EMPTY_REGEX
|
|
350
371
|
};
|