topbit 2.0.0 → 3.0.0

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 (72) hide show
  1. package/README.cn.md +133 -176
  2. package/README.md +803 -592
  3. package/bin/new-ctl.js +7 -3
  4. package/demo/allow.js +13 -13
  5. package/demo/controller/api.js +15 -0
  6. package/demo/extends.js +5 -0
  7. package/demo/http2.js +34 -0
  8. package/demo/http2_proxy_backend.js +45 -0
  9. package/demo/http2proxy.js +48 -0
  10. package/demo/http_proxy_backend.js +44 -0
  11. package/demo/httpproxy.js +47 -0
  12. package/demo/loader.js +27 -0
  13. package/demo/log.js +1 -1
  14. package/demo/memlimit.js +1 -1
  15. package/demo/min.js +1 -1
  16. package/demo/serv.js +1 -1
  17. package/images/topbit-middleware.png +0 -0
  18. package/images/topbit.png +0 -0
  19. package/package.json +7 -6
  20. package/src/_loadExtends.js +21 -0
  21. package/src/bodyparser.js +1 -1
  22. package/src/context1.js +19 -19
  23. package/src/context2.js +11 -8
  24. package/src/ctxpool.js +1 -0
  25. package/src/extends/Http2Pool.js +365 -0
  26. package/src/extends/__randstring.js +24 -0
  27. package/src/extends/cookie.js +44 -0
  28. package/src/extends/cors.js +334 -0
  29. package/src/extends/errorlog.js +252 -0
  30. package/src/extends/http2limit.js +126 -0
  31. package/src/extends/http2proxy.js +691 -0
  32. package/src/extends/jwt.js +217 -0
  33. package/src/extends/mixlogger.js +63 -0
  34. package/src/extends/paramcheck.js +266 -0
  35. package/src/extends/proxy.js +662 -0
  36. package/src/extends/realip.js +34 -0
  37. package/src/extends/referer.js +68 -0
  38. package/src/extends/resource.js +398 -0
  39. package/src/extends/session.js +174 -0
  40. package/src/extends/setfinal.js +50 -0
  41. package/src/extends/sni.js +48 -0
  42. package/src/extends/sse.js +293 -0
  43. package/src/extends/timing.js +111 -0
  44. package/src/extends/tofile.js +123 -0
  45. package/src/http1.js +15 -16
  46. package/src/http2.js +5 -5
  47. package/src/httpc.js +3 -3
  48. package/src/lib/npargv.js +354 -0
  49. package/src/lib/zipdata.js +45 -0
  50. package/src/middleware1.js +15 -16
  51. package/src/middleware2.js +4 -9
  52. package/src/token/token.js +4 -5
  53. package/src/topbit.js +13 -11
  54. package/test/{test-helper.js → test-ext.js} +1 -1
  55. package/test/test-route.js +1 -1
  56. package/cache/allow.js +0 -130
  57. package/cache/errserv.js +0 -45
  58. package/cache/minserv.js +0 -167
  59. package/cache/router.js +0 -84
  60. package/cache/servsock.js +0 -286
  61. package/cache/sni.js +0 -66
  62. package/images/titbit-middleware.png +0 -0
  63. package/images/titbit.png +0 -0
  64. package/tmp/buff-code +0 -134
  65. package/tmp/devplan +0 -9
  66. package/tmp/evt-test.js +0 -34
  67. package/tmp/fastParseUrl.js +0 -302
  68. package/tmp/router-rule.js +0 -559
  69. package/tmp/test-cdps.js +0 -122
  70. package/tmp/titbit.js +0 -1286
  71. /package/{cache/rsa → demo/cert}/localhost-cert.pem +0 -0
  72. /package/{cache/rsa → demo/cert}/localhost-privkey.pem +0 -0
package/bin/new-ctl.js CHANGED
@@ -79,10 +79,14 @@ function fmt_ctx_param(text) {
79
79
  const fs = require('fs')
80
80
 
81
81
  function makeController(name) {
82
+ let modname = name
83
+ if (!(/^[A-Z].*/).test(name)) {
84
+ modname = name.substring(0, 1).toUpperCase() + name.substring(1)
85
+ }
82
86
 
83
87
  return `'use strict'\n\n`
84
88
  + (simple_mode ? '' : head_hint)
85
- + `class ${name} {\n\n`
89
+ + `class ${modname} {\n\n`
86
90
  + ` constructor() {\n`
87
91
  + ` //param用于指定最后的路由参数,默认就是/:id\n`
88
92
  + ` //若要改变路由,则可以设置此属性,比如设置为/:name\n`
@@ -109,7 +113,7 @@ function makeController(name) {
109
113
  + `${fmt_ctx_param('删除资源')}`
110
114
  + ` async _delete(ctx) {\n\n`
111
115
  + ` }\n`
112
- + `}\n\nmodule.exports = ${name}\n`
116
+ + `}\n\nmodule.exports = ${modname}\n`
113
117
  + (simple_mode ? '' : `${type_context}`)
114
118
 
115
119
  }
@@ -121,7 +125,7 @@ let limitName = [
121
125
  'typeof', 'continue', 'fetch', 'globalThis', 'queueMicrotask'
122
126
  ]
123
127
 
124
- let name_preg = /^[a-z_][a-z0-9_\-]{0,50}$/i
128
+ let name_preg = /^[A-Za-z_][A-Za-z0-9_\-]{0,50}$/i
125
129
 
126
130
  function checkName (name) {
127
131
  return name_preg.test(name)
package/demo/allow.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const titbit = require('../lib/titbit.js');
3
+ const titbit = require('../src/topbit.js');
4
4
 
5
5
  const app = new titbit({
6
6
  debug : true,
@@ -20,29 +20,29 @@ const app = new titbit({
20
20
  app.use(async (c, next) => {
21
21
  console.log('pre middleware start');
22
22
  console.log(c.group, c.name, c.method, c.path);
23
- await next();
23
+ await next(c);
24
24
  console.log('pre middleware end');
25
25
  }, {pre : true});
26
26
 
27
27
  app.use(async (c, next) => {
28
28
  console.log('middleware 1');
29
- await next();
29
+ await next(c);
30
30
  console.log('middleware 1 end');
31
31
  }, 'home');
32
32
 
33
33
  app.use(async (c, next) => {
34
34
  console.log('middleware 2');
35
35
  c.body.say = '你好';
36
- await next();
36
+ await next(c);
37
37
  console.log('middleware 2 end');
38
38
  }, {group: 'test', method: 'POST'});
39
39
 
40
40
  app.use(async (c, next) => {
41
41
  console.log('middleware 3');
42
42
  if (c.query.say == 'hey') {
43
- c.send('你好,test 接口 GET请求结束。');
43
+ c.to('你好,test 接口 GET请求结束。');
44
44
  } else {
45
- await next();
45
+ await next(c);
46
46
  }
47
47
  console.log('middleware 3 end');
48
48
  }, {group: 'test', method : 'GET'});
@@ -50,30 +50,30 @@ app.use(async (c, next) => {
50
50
  app.use(async (c, next) => {
51
51
  console.log('set body size');
52
52
  c.maxBody = 24;
53
- await next();
53
+ await next(c);
54
54
  }, {name: 'test-post', pre: true});
55
55
 
56
56
  app.use(async (c, next) => {
57
57
  console.log('middleware 4');
58
58
  c.body.x = 12;
59
- await next();
59
+ await next(c);
60
60
  console.log('middleware 4 end');
61
61
  }, 'test-post');
62
62
 
63
63
  app.get('/', async c => {
64
- c.send('ok');
64
+ c.to('ok');
65
65
  }, 'home');
66
66
 
67
67
  app.get('/test', async c => {
68
- c.send(c.name);
68
+ c.to(c.name);
69
69
  }, {group: 'test', name : 'test'});
70
70
 
71
71
  app.post('/test', async c => {
72
- c.send(c.body);
72
+ c.to(c.body);
73
73
  }, {group: 'test', name : 'test-post'});
74
74
 
75
75
  app.post('/transmit', async c => {
76
- c.send('ok');
76
+ c.to('ok');
77
77
  }, 'transmit');
78
78
 
79
79
  app.use(async (c, next) => {
@@ -88,7 +88,7 @@ app.use(async (c, next) => {
88
88
  }
89
89
  };
90
90
 
91
- await next();
91
+ await next(c);
92
92
 
93
93
  console.log(total, 'bytes');
94
94
 
@@ -0,0 +1,15 @@
1
+ 'use strict'
2
+
3
+ class Api {
4
+ constructor() {
5
+
6
+ }
7
+
8
+ async get(ctx) {
9
+ ctx.ok([
10
+ ctx.method, ctx.path, ctx.routepath, ctx.param
11
+ ])
12
+ }
13
+ }
14
+
15
+ module.exports = Api
@@ -0,0 +1,5 @@
1
+ 'use strict'
2
+
3
+ let toolkit = require('../src/topbit.js').extensions
4
+
5
+ console.log(toolkit)
package/demo/http2.js ADDED
@@ -0,0 +1,34 @@
1
+ 'use strict'
2
+
3
+ 'use strict'
4
+
5
+ process.chdir(__dirname)
6
+
7
+ let Topbit = require('../src/topbit.js')
8
+ let {Loader} = Topbit
9
+
10
+ let app = new Topbit({
11
+ debug: true,
12
+ globalLog: true,
13
+ logType: 'stdio',
14
+ loadInfoFile: '--mem',
15
+ cert: './cert/localhost-cert.pem',
16
+ key: './cert/localhost-privkey.pem',
17
+ http2: true
18
+ })
19
+
20
+ if (app.isWorker) {
21
+ app.get('/', async ctx => {
22
+ ctx.ok('ok')
23
+ })
24
+
25
+ let ld = new Loader()
26
+
27
+ ld.init(app)
28
+ }
29
+
30
+ app.sched('none')
31
+
32
+ app.autoWorker(3)
33
+
34
+ app.daemon(1234, 1)
@@ -0,0 +1,45 @@
1
+ 'use strict'
2
+
3
+ const Topbit = require('../src/topbit.js')
4
+
5
+ const app = new Topbit({
6
+ debug: true,
7
+ http2: true,
8
+ loadInfoFile: '/tmp/loadinfo.log',
9
+ globalLog: true,
10
+ monitorTimeSlice: 512,
11
+ timeout: 0
12
+ })
13
+
14
+ app.use(async (c, next) => {
15
+ c.setHeader('x-set-key', `${parseInt(Math.random() * 10000) + Date.now()}`)
16
+ await next(c)
17
+ })
18
+
19
+ app.get('/header', async c => {
20
+ c.to(c.headers)
21
+ })
22
+
23
+ app.get('/', async c => {
24
+ c.to(Math.random())
25
+ })
26
+
27
+ app.get('/:name/:age/:mobile/:info', async c => {
28
+ c.to(c.param)
29
+ })
30
+
31
+ app.post('/p', async c => {
32
+ c.to(c.body)
33
+ })
34
+
35
+ let port = 2022
36
+ let port_ind = process.argv.indexOf('--port')
37
+
38
+ if (port_ind > 0 && port_ind < process.argv.length - 1) {
39
+ port = parseInt(process.argv[port_ind + 1])
40
+
41
+ if (typeof port !== 'number')
42
+ port = 2022
43
+ }
44
+
45
+ app.run(port)
@@ -0,0 +1,48 @@
1
+ 'use strict'
2
+
3
+ let Topbit = require('../src/topbit.js')
4
+
5
+ let {Http2Proxy} = Topbit.extensions
6
+
7
+ let app = new Topbit({
8
+ debug: true,
9
+ globalLog: true,
10
+ loadInfoFile: '--mem',
11
+ http2: true
12
+ })
13
+
14
+ if (app.isWorker) {
15
+ let h2proxy = new Http2Proxy({
16
+ config: {
17
+ 'x.com': [
18
+ {
19
+ url: 'http://localhost:3001',
20
+ weight: 10,
21
+ path : '/',
22
+ reconnDelay: 200,
23
+ max: 2,
24
+ headers: {
25
+ 'x-test-key': `${Date.now()}-${Math.random()}`
26
+ },
27
+ connectTimeout: 2000
28
+ },
29
+
30
+ {
31
+ url: 'http://localhost:3002',
32
+ weight: 4,
33
+ path : '/',
34
+ max: 2,
35
+ reconnDelay: 100,
36
+ headers: {
37
+ 'x-test-key2': `${Date.now()}-${Math.random()}`
38
+ }
39
+ }
40
+ ]
41
+ },
42
+ debug: true
43
+ })
44
+
45
+ h2proxy.init(app)
46
+ }
47
+
48
+ app.daemon(1234, 2)
@@ -0,0 +1,44 @@
1
+ 'use strict'
2
+
3
+ const Topbit = require('../src/topbit.js')
4
+
5
+ const app = new Topbit({
6
+ debug: true,
7
+ loadInfoFile: '/tmp/loadinfo.log',
8
+ globalLog: true,
9
+ monitorTimeSlice: 512,
10
+ timeout: 100000
11
+ })
12
+
13
+ app.use(async (c, next) => {
14
+ c.setHeader('x-set-key', `${parseInt(Math.random() * 10000) + Date.now()}`)
15
+ await next(c)
16
+ })
17
+
18
+ app.get('/header', async c => {
19
+ c.to(c.headers)
20
+ })
21
+
22
+ app.get('/', async c => {
23
+ c.to(Math.random())
24
+ })
25
+
26
+ app.get('/:name/:age/:mobile/:info', async c => {
27
+ c.to(c.param)
28
+ })
29
+
30
+ app.post('/p', async c => {
31
+ c.to(c.body)
32
+ })
33
+
34
+ let port = 2022
35
+ let port_ind = process.argv.indexOf('--port')
36
+
37
+ if (port_ind > 0 && port_ind < process.argv.length - 1) {
38
+ port = parseInt(process.argv[port_ind + 1])
39
+
40
+ if (typeof port !== 'number')
41
+ port = 2022
42
+ }
43
+
44
+ app.run(port)
@@ -0,0 +1,47 @@
1
+ 'use strict'
2
+
3
+ let Topbit = require('../src/topbit.js')
4
+
5
+ let {Proxy} = Topbit.extensions
6
+
7
+ let app = new Topbit({
8
+ debug: true,
9
+ globalLog: true,
10
+ loadInfoFile: '--mem',
11
+ })
12
+
13
+ if (app.isWorker) {
14
+ let pxy = new Proxy({
15
+ config: {
16
+ 'x.com': [
17
+ {
18
+ url: 'http://localhost:3001',
19
+ weight: 10,
20
+ path : '/',
21
+ reconnDelay: 200,
22
+ max: 2,
23
+ headers: {
24
+ 'x-test-key': `${Date.now()}-${Math.random()}`
25
+ },
26
+ connectTimeout: 2000
27
+ },
28
+
29
+ {
30
+ url: 'http://localhost:3002',
31
+ weight: 4,
32
+ path : '/',
33
+ max: 2,
34
+ reconnDelay: 100,
35
+ headers: {
36
+ 'x-test-key2': `${Date.now()}-${Math.random()}`
37
+ }
38
+ }
39
+ ]
40
+ },
41
+ debug: true
42
+ })
43
+
44
+ pxy.init(app)
45
+ }
46
+
47
+ app.daemon(1234, 2)
package/demo/loader.js ADDED
@@ -0,0 +1,27 @@
1
+ 'use strict'
2
+
3
+ process.chdir(__dirname)
4
+
5
+ let Topbit = require('../src/topbit.js')
6
+ let {Loader} = Topbit
7
+
8
+ let app = new Topbit({
9
+ debug: true,
10
+ //loadInfoFile: '--mem',
11
+ })
12
+
13
+ if (app.isWorker) {
14
+ app.get('/', async ctx => {
15
+ ctx.ok('ok')
16
+ })
17
+
18
+ let ld = new Loader()
19
+
20
+ ld.init(app)
21
+ }
22
+
23
+ app.sched('none')
24
+
25
+ app.autoWorker(3)
26
+
27
+ app.daemon(1234, 1)
package/demo/log.js CHANGED
@@ -1,4 +1,4 @@
1
- const titbit = require('../lib/titbit.js');
1
+ const titbit = require('../src/topbit.js');
2
2
  const v8 = require('v8');
3
3
  const cluster = require('cluster');
4
4
 
package/demo/memlimit.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const titbit = require('../lib/titbit.js');
3
+ const titbit = require('../src/topbit.js');
4
4
 
5
5
  const cluster = require('cluster');
6
6
 
package/demo/min.js CHANGED
@@ -1,4 +1,4 @@
1
- const titbit = require('../lib/titbit.js');
1
+ const titbit = require('../src/topbit.js');
2
2
 
3
3
  const app = new titbit({
4
4
  debug: true
package/demo/serv.js CHANGED
@@ -1,4 +1,4 @@
1
- const titbit = require('../lib/titbit.js')
1
+ const titbit = require('../src/topbit.js')
2
2
 
3
3
  const app = new titbit({
4
4
  debug: true,
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "topbit",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "A Server-side web framework support http/1.1 and http/2",
5
5
  "main": "src/topbit.js",
6
6
  "directories": {
@@ -8,10 +8,11 @@
8
8
  },
9
9
  "bin": {
10
10
  "tbload": "bin/loadinfo.sh",
11
- "topbit-new-app": "bin/newapp.js"
11
+ "topbit-new-app": "bin/newapp.js",
12
+ "new-route": "bin/new-ctl.js"
12
13
  },
13
14
  "scripts": {
14
- "test": "node test/test-service.js",
15
+ "test": "node test/test-ext.js",
15
16
  "test-route": "node test/test-route.js"
16
17
  },
17
18
  "keywords": [
@@ -32,6 +33,9 @@
32
33
  "framework",
33
34
  "tidbit",
34
35
  "proxy",
36
+ "http2proxy",
37
+ "mvc",
38
+ "api",
35
39
  "webserver",
36
40
  "backend",
37
41
  "serverside"
@@ -47,9 +51,6 @@
47
51
  "license": "ISC",
48
52
  "author": "BraveWang",
49
53
  "type": "commonjs",
50
- "scripts": {
51
- "test": "echo ok"
52
- },
53
54
  "dependencies": {
54
55
 
55
56
  },
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs')
4
+
5
+ let files = fs.readdirSync(`${__dirname}/extends/`, {withFileTypes: true})
6
+
7
+ for (let i = 0; i < files.length; i++) {
8
+
9
+ if (!files[i].isFile()) continue
10
+
11
+ if (files[i].name.indexOf('.js') < 0) continue
12
+
13
+ if (files[i].name.substring(files[i].name.length - 3) !== '.js') continue
14
+
15
+ if (files[i].name[0] === '_' || files[i].name[0] === '!') continue
16
+
17
+ let modname = files[i].name.substring(0, files[i].name.length-3)
18
+
19
+ let em = require('./extends/'+files[i].name)
20
+ exports[em.name] = em
21
+ }
package/src/bodyparser.js CHANGED
@@ -412,7 +412,7 @@ class Bodyparser {
412
412
  }
413
413
  }
414
414
 
415
- await next();
415
+ await next(ctx);
416
416
  };
417
417
  }
418
418
  }
package/src/context1.js CHANGED
@@ -57,11 +57,8 @@ class Context {
57
57
  this.data = ''
58
58
  this.dataEncoding = 'utf8'
59
59
 
60
- this.request = null
61
-
62
- this.response = null
63
-
64
- this.reply = null
60
+ this.req = null
61
+ this.res = null
65
62
 
66
63
  this.box = {}
67
64
 
@@ -71,18 +68,18 @@ class Context {
71
68
  }
72
69
 
73
70
  json(data) {
74
- return this.setHeader('content-type', 'application/json').send(data)
71
+ return this.setHeader('content-type', 'application/json').to(data)
75
72
  }
76
73
 
77
74
  text(data, encoding='utf-8') {
78
- return this.setHeader('content-type', `text/plain;charset=${encoding}`).send(data)
75
+ return this.setHeader('content-type', `text/plain;charset=${encoding}`).to(data)
79
76
  }
80
77
 
81
78
  html(data, encoding='utf-8') {
82
- return this.setHeader('content-type', `text/html;charset=${encoding}`).send(data)
79
+ return this.setHeader('content-type', `text/html;charset=${encoding}`).to(data)
83
80
  }
84
81
 
85
- send(d) {
82
+ to(d) {
86
83
  this.data = d
87
84
  }
88
85
 
@@ -103,25 +100,25 @@ class Context {
103
100
  }
104
101
 
105
102
  setHeader(name, val) {
106
- this.response.setHeader(name, val)
103
+ this.res.setHeader(name, val)
107
104
  return this
108
105
  }
109
106
 
110
107
  sendHeader() {
111
- !this.response
112
- && !this.response.headersSent
113
- && this.response.writeHead(this.response.statusCode)
108
+ !this.res
109
+ && !this.res.headersSent
110
+ && this.res.writeHead(this.res.statusCode)
114
111
 
115
112
  return this
116
113
  }
117
114
 
118
115
  status(stcode = null) {
119
116
  if (stcode === null) {
120
- return this.response.statusCode
117
+ return this.res.statusCode
121
118
  }
122
119
 
123
- if (this.response) {
124
- this.response.statusCode = stcode
120
+ if (this.res) {
121
+ this.res.statusCode = stcode
125
122
  }
126
123
 
127
124
  return this
@@ -136,7 +133,7 @@ class Context {
136
133
  * @param {object} options
137
134
  * */
138
135
  pipe(filename, options={}) {
139
- return ext.pipe(filename, this.reply, options)
136
+ return ext.pipe(filename, this.res, options)
140
137
  }
141
138
 
142
139
  pipeJson(filename) {
@@ -152,15 +149,18 @@ class Context {
152
149
  }
153
150
 
154
151
  removeHeader(name) {
155
- this.reply.removeHeader(name)
152
+ this.res.removeHeader(name)
156
153
  return this
157
154
  }
158
155
 
159
156
  write(data) {
160
- this.reply.write(data)
157
+ this.res.write(data)
161
158
  return this
162
159
  }
163
160
 
164
161
  }
165
162
 
163
+ Context.prototype.oo = Context.prototype.to
164
+ Context.prototype.ok = Context.prototype.to
165
+
166
166
  module.exports = Context
package/src/context2.js CHANGED
@@ -60,9 +60,9 @@ class Context {
60
60
  //在请求时指向实际的stream
61
61
  this.stream = null
62
62
 
63
- this.request = null
63
+ this.req = null
64
64
 
65
- this.reply = null
65
+ this.res = null
66
66
 
67
67
  this.box = {}
68
68
 
@@ -72,18 +72,18 @@ class Context {
72
72
  }
73
73
 
74
74
  json(data) {
75
- return this.setHeader('content-type', 'application/json').send(data)
75
+ return this.setHeader('content-type', 'application/json').to(data)
76
76
  }
77
77
 
78
78
  text(data, encoding='utf-8') {
79
- return this.setHeader('content-type', `text/plain;charset=${encoding}`).send(data)
79
+ return this.setHeader('content-type', `text/plain;charset=${encoding}`).to(data)
80
80
  }
81
81
 
82
82
  html(data, encoding='utf-8') {
83
- return this.setHeader('content-type', `text/html;charset=${encoding}`).send(data)
83
+ return this.setHeader('content-type', `text/html;charset=${encoding}`).to(data)
84
84
  }
85
85
 
86
- send(d) {
86
+ to(d) {
87
87
  this.data = d
88
88
  }
89
89
 
@@ -147,7 +147,7 @@ class Context {
147
147
  this.sendHeader()
148
148
  }
149
149
 
150
- return ext.pipe(filename, this.reply, options)
150
+ return ext.pipe(filename, this.res, options)
151
151
  }
152
152
 
153
153
  pipeJson(filename) {
@@ -170,10 +170,13 @@ class Context {
170
170
  write(data) {
171
171
  this.stream && !this.stream.headersSent && this.stream.respond(this.dataHeaders)
172
172
 
173
- this.reply.write(data)
173
+ this.stream.write(data)
174
174
  return this
175
175
  }
176
176
 
177
177
  }
178
178
 
179
+ Context.prototype.oo = Context.prototype.to
180
+ Context.prototype.ok = Context.prototype.to
181
+
179
182
  module.exports = Context
package/src/ctxpool.js CHANGED
@@ -10,6 +10,7 @@ let ctxpool = new function () {
10
10
  if (this.pool.length > 0) {
11
11
  return this.pool.pop()
12
12
  }
13
+
13
14
  return null
14
15
  }
15
16