upcore-tcp 0.0.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.
package/README.md ADDED
@@ -0,0 +1,318 @@
1
+ # upcore
2
+
3
+ Framework Node.js ขนาดเล็กสำหรับ **HTTP + WebSocket**
4
+ ออกแบบมาให้เร็ว เบา และควบคุม flow ได้เองทั้งหมด
5
+ ใช้ **CommonJS** และรองรับทั้ง **Node.js** และ **Bun.js**
6
+
7
+ > Created by **R938**
8
+
9
+ ---
10
+
11
+ ## ✨ ความสามารถหลัก
12
+
13
+ - สร้าง **HTTP Server + WebSocket Server** จาก instance เดียว
14
+ - Routing ครบ:
15
+ - static path
16
+ - dynamic params (`:id`)
17
+ - wildcard (`*`)
18
+ - Middleware chaining ด้วย `next()`
19
+ - Domain-based routing (`app.domain(...)`)
20
+ - Parse body ได้หลายรูปแบบ:
21
+ - `json`
22
+ - `formdata`
23
+ - `wwwform`
24
+ - `binary`
25
+ - ระบบ **Type & Validation** ในตัว
26
+ - Static file serving (`directory`)
27
+ - Auto route loader จากไฟล์ (`routes`)
28
+ - Helper ครบ:
29
+ - cookie
30
+ - query
31
+ - redirect
32
+ - file response
33
+ - ใช้งานได้ทั้ง **Node.js** และ **Bun.js**
34
+
35
+
36
+ ## ติดตั้งใช้งาน
37
+ ```bash
38
+ npm install upcore
39
+ ```
40
+
41
+ ## Quick Start
42
+ ```js
43
+ const upcore = require('upcore');
44
+
45
+ const app = new upcore({
46
+ dev: true,
47
+ logger: true
48
+ });
49
+
50
+ app.get('/', (req) => {
51
+ req.html('<h1>Hello upcore</h1>');
52
+ });
53
+
54
+ app.listen();
55
+ ```
56
+
57
+ ## โครงสร้างเริ่มต้น (แนะนำ)
58
+ ```text
59
+ project/
60
+ routes/
61
+ get@.js
62
+ post@api-login.js
63
+ get@user-$id.js
64
+ public/
65
+ index.html
66
+ logo.png
67
+ app.js
68
+ ```
69
+
70
+ ตัวอย่าง `app.js`:
71
+ ```js
72
+ const upcore = require('upcore');
73
+
74
+ const app = new upcore({ port: 3000, dev: true, logger: true });
75
+
76
+ app
77
+ .add('json', ({ load, next }) => load('json', next))
78
+ .directory('./public', { pathname: '/static', type: ['png', 'jpg', 'html', 'css', 'js'] })
79
+ .routes('./routes');
80
+
81
+ app.listen();
82
+ ```
83
+
84
+ ## การตั้งค่า `new upcore(options)`
85
+ - `hostname` default: `0.0.0.0`
86
+ - `port` default: `3000`
87
+ - `keepAlive` default: `true`
88
+ - `keepAliveInitialDelay` default: `5000`
89
+ - `noDelay` default: `true`
90
+ - `timeout` default: `30000`
91
+ - `dev` default: `false` (ถ้า `true` จะพิมพ์ข้อความตอน listen)
92
+ - `logger` default: `false` (ถ้า `true` จะ log traffic)
93
+ - `use: 'https'` ใช้ `tls` แทน `net` (ต้องส่ง options ที่จำเป็นสำหรับ TLS)
94
+
95
+ ## API หลักของ App
96
+ - `app.listen(callback?)`
97
+ - `app.type(id, rule)`
98
+ - `app.types(rules)`
99
+ - `app.add(id, middleware)`
100
+ - `app.domain(domain | string[])`
101
+ - `app.next(...middlewareOrName)`
102
+ - `app.all(path, ...handler)`
103
+ - `app.get/post/put/patch/delete/options/head(path, ...handler)`
104
+ - `app.websocket(path, onStart, onData?, onClose?, onPing?, onPong?)`
105
+ - `app.directory(dir, options)`
106
+ - `app.routes(dir)`
107
+
108
+ ## API ใน Handler (HTTP)
109
+ - Response: `req.code()`, `req.set()`, `req.http()`, `req.send()`, `req.html()`, `req.json()`, `req.redirect()`, `req.file()`, `req.error()`
110
+ - Request: `req.get(headerKey)`, `req.look(queryKeys)`, `req.u(queryKey)`, `req.load()`, `req.check()`, `req.q()`
111
+ - Cookie: `req.gcookie()`, `req.scookie()`, `req.rcookie()`
112
+ - Middleware flow: `req.next()`
113
+ - Validation: `req.test({ type, data, core })`
114
+
115
+ ## API ใน Handler (WebSocket)
116
+ - `req.start(uid)` เริ่ม handshake + ลงทะเบียนผู้ใช้
117
+ - `req.stop()`
118
+ - `req.send()`, `req.sends()`, `req.sendto()`, `req.sendc()`
119
+ - `req.json()`, `req.jsons()`, `req.jsonto()`, `req.jsonc()`
120
+ - `req.ping()`, `req.pong()`
121
+ - `req.userEach()`, `req.userHas()`, `req.userLength()`
122
+
123
+ ## ระบบ Type/Validation
124
+ รองรับ `type`:
125
+ - `int`
126
+ - `string`
127
+ - `boolean`
128
+ - `array`
129
+ - `object`
130
+ - `file` (object ที่มี `data` เป็น Buffer)
131
+ - `timestamp` (`YYYY-MM-DDTHH:mm:ss.sssZ`)
132
+
133
+ รองรับเงื่อนไขเสริม:
134
+ - `length: [min, max]` หรือ `length: exact`
135
+ - `format: 'user' | 'email' | 'hex' | 'decimal'`
136
+ - `list: [...]`
137
+ - `mime: [...]` (ใช้กับ `file`)
138
+
139
+ ตัวอย่าง:
140
+ ```js
141
+ app
142
+ .type('id', { type: 'int', length: [1, 1000000] })
143
+ .type('email', { type: 'string', format: 'email', length: [6, 300] })
144
+ .type('avatar', { type: 'file', mime: ['image/png', 'image/jpeg'] });
145
+ .types({
146
+ userid:{ type: 'int', length: [1, 1000000] }
147
+ no:{ type: 'int', length: [1, 1000] }
148
+ });
149
+ ```
150
+
151
+ ## ตัวอย่างการใช้งานทุกเคสหลัก
152
+
153
+ ### 1) GET/POST พื้นฐาน
154
+ ```js
155
+ app.get('/', (req) => req.send('ok'));
156
+ app.post('/echo', ({ load, json, body }) => {
157
+ load('json', () => json({ body }));
158
+ });
159
+ ```
160
+
161
+ ### 2) Dynamic params และ wildcard
162
+ ```js
163
+ app.get('/user/:id', ({ params, json }) => json({userid: params.id}));
164
+ // [GET] /user/1
165
+ // {"userid":1}
166
+ app.get('/files/*', ({ wildcard, send }) => send(wildcard));
167
+ // [GET] /files/asset/logo.svg
168
+ // /asset/logo.svg
169
+ ```
170
+
171
+ ### 3) Query
172
+ ```js
173
+ app.get('/search', (req) => {
174
+ if (!req.look('q')) return req.code(400).send('missing q');
175
+ req.json({ q: req.u('q') });
176
+ });
177
+ // [GET] /search?q=upcore
178
+ // {"q":"upcore"}
179
+ ```
180
+
181
+ ### 4) Middleware chain
182
+ ```js
183
+ app.add('auth', (req) => {
184
+ if (!req.look('token')) return req.code(401).send('unauthorized');
185
+ req.next();
186
+ });
187
+
188
+ app.next('auth').get('/private', (req) => req.send('ok'));
189
+ ```
190
+
191
+ ### 5) Parse body หลายประเภท
192
+ ```js
193
+ app.add('json', ({ load, next }) => load('json', next));
194
+ app.add('form', ({ load, next }) => load(['wwwform', 'formdata'], next));
195
+ ```
196
+
197
+ ### 6) Validation ด้วย `req.test`
198
+ ```js
199
+ app.type('id', { type: 'int', length: [1, 999999] });
200
+
201
+ app.post('/user', 'json', (req) => {
202
+ const data = req.test({
203
+ type: 'json',
204
+ data: { id: { t: 'id', r: true } }
205
+ });
206
+
207
+ if (data !== false) req.json({ ok: true, data });
208
+ });
209
+ ```
210
+
211
+ ### 7) Cookie
212
+ ```js
213
+ app.get('/cookie/set', (req) => {
214
+ req.scookie('sid', 'abc123', { path: '/', httponly: true, 'max-age': 3600 });
215
+ req.send('set');
216
+ });
217
+
218
+ app.get('/cookie/get', (req) => req.json({ sid: req.gcookie('sid') || null }));
219
+ app.get('/cookie/remove', (req) => { req.rcookie('sid', { path: '/' }); req.send('removed'); });
220
+ ```
221
+
222
+ ### 8) Redirect
223
+ ```js
224
+ app.get('/go-doc', (req) => req.redirect('/docs')); // 301
225
+ app.get('/go-login', (req) => req.redirect(302, '/login'));
226
+ ```
227
+
228
+ ### 9) ส่งไฟล์ / template parameter
229
+ ```js
230
+ app.get('/download', (req) => {
231
+ req.file({ file: './public/a.jpg', download: 'photo.jpg' });
232
+ });
233
+
234
+ app.get('/page', (req) => {
235
+ req.file({
236
+ file: './public/index.html',
237
+ parameter: { title: 'Hello', time: new Date().toISOString() }
238
+ });
239
+ });
240
+ ```
241
+ ตัวอย่างไฟล์ /public/index.html
242
+ ```html
243
+ <h1>{{title}}</h1>
244
+ <p>Time: {{time}}</p>
245
+ ```
246
+
247
+ ### 10) Static directory
248
+ ```js
249
+ app.directory('./public', {
250
+ pathname: '/assets',
251
+ type: ['png', 'jpg', 'mp4', 'html', 'css', 'js']
252
+ });
253
+ ```
254
+
255
+ ### 11) Auto routes จากไฟล์
256
+ ```js
257
+ app.routes('./routes');
258
+ ```
259
+
260
+ หลักการตั้งชื่อไฟล์ของ `app.routes`:
261
+ - อ่านเฉพาะไฟล์ `.js` ในโฟลเดอร์นั้น (ไม่อ่านโฟลเดอร์ย่อย)
262
+ - รูปแบบหลัก: `method@path.js`
263
+ - ถ้าไม่มี `@` จะถือเป็น `GET` อัตโนมัติ เช่น `home.js` => `GET /home`
264
+ - `method` ที่ใช้ได้ตาม API: `get`, `post`, `put`, `patch`, `delete`, `options`, `head`, `all`, `websocket`
265
+ - `path` จะถูกแปลงอักขระพิเศษ:
266
+ - `-` => `/`
267
+ - `$` => `:` (path param)
268
+ - `\"` จะถูกลบออกจาก path
269
+ - ถ้าชื่อไฟล์มี `@` มากกว่า 1 ตัว (เช่น `get@a@b.js`) ระบบจะไม่ map route ไฟล์นั้น
270
+
271
+ ตัวอย่างชื่อไฟล์ -> endpoint:
272
+ - `get@home-admin.js` => `GET /home/admin`
273
+ - `get@user-$id.js` => `GET /user/:id`
274
+ - `post@auth-login.js` => `POST /auth/login`
275
+ - `health.js` => `GET /health`
276
+ - `all@api-v1-*.js` => `ALL /api/v1/*`
277
+
278
+ ตัวอย่างไฟล์ route:
279
+ ```js
280
+ // routes/post@user-login.js
281
+ module.exports = (req) => {
282
+ req.json({ ok: true, body: req.body });
283
+ };
284
+ module.exports.next = 'json'; // ใช้ middleware ที่ add ไว้แล้ว
285
+ // module.exports.next = ['auth', 'json'];
286
+ // module.exports.next = ()=>{};
287
+ ```
288
+
289
+ ### 12) Domain-based routing
290
+ ```js
291
+ app.domain(['api.local', 'localhost'])
292
+ .get('/health', (req) => req.json({ ok: true }));
293
+
294
+ // [GET] http://api.local/health
295
+ // [GET] http://localhost/health
296
+
297
+ // {"ok":true}
298
+ ```
299
+
300
+ ### 13) WebSocket
301
+ ```js
302
+ app.websocket('/ws', async (req) => {
303
+ const ok = await req.start(req.u('uid') || String(Date.now()));
304
+ if (!ok) return req.stop();
305
+ req.send('connected');
306
+ }, (req) => {
307
+ req.send(req.body.payload); // echo
308
+ }, (req) => {
309
+ console.log('socket closed', req.uid);
310
+ });
311
+
312
+ // ws://127.0.0.1/ws?uid=upcore
313
+ ```
314
+ ## หมายเหตุสำคัญ
315
+ - ค่า `Host` ต้องตรงกับ domain ที่ลง route (หรือใช้ `*` ถ้าต้องการรับทุก host)
316
+ - ถ้า `req.load(...)` ประเภทไม่ตรงกับ `Content-Type` จะตอบ 400
317
+ - `req.test(...)` จะตอบ error อัตโนมัติเมื่อ type/format ไม่ผ่าน
318
+ - ถ้าใช้ `app.routes(dir)` โฟลเดอร์ต้องมีไฟล์ `.js` ตาม naming convention
package/index.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./lib/server');
@@ -0,0 +1,7 @@
1
+ module.exports = function(name){
2
+ try{
3
+ name = decodeURIComponent(name);
4
+ return name.replace(/(\.\.)|[\[\n\r\t\*\?\"\<\>\|]/,'') == name ? name : false;
5
+ }catch(e){}
6
+ return false;
7
+ }
@@ -0,0 +1,120 @@
1
+ module.exports = function(chunk){
2
+ const len = chunk.length;
3
+ if (len < 16) return false;
4
+ let p = 0;
5
+
6
+ // ---------- METHOD ----------
7
+ let method = false;
8
+
9
+ switch((chunk[0] << 24) | (chunk[1] << 16) | (chunk[2] << 8) | chunk[3]){
10
+ case 0x47455420: method = 'get'; p = 4; break; // "GET "
11
+ case 0x504F5354: method = 'post'; p = 5; break; // "POST"
12
+ case 0x50555420: method = 'put'; p = 4; break; // "PUT "
13
+ case 0x48454144: method = 'head'; p = 5; break; // "HEAD"
14
+ case 0x44454C45: method = 'delete'; p = 7; break; // "DELE"
15
+ case 0x4F505449: method = 'options'; p = 8; break; // "OPTI"
16
+ case 0x50415443: method = 'patch'; p = 6; break; // "PATC"
17
+ default: return false;
18
+ }
19
+
20
+ // ---------- PATH ----------
21
+ const pathStart = p;
22
+ let searchPoint = -1;
23
+
24
+ while(p < len && chunk[p] !== 32){
25
+ if (chunk[p] === 63 && searchPoint === -1) searchPoint = p;
26
+ p++;
27
+ }
28
+ if (p >= len) return false;
29
+
30
+ const pathEnd = p++;
31
+
32
+ p += 10;
33
+
34
+ // ---------- HEADERS ----------
35
+ let header = [];
36
+ let host = false;
37
+ let port = false;
38
+ let length = 0;
39
+ let type = false;
40
+ let connection = 0;
41
+
42
+
43
+ while(p + 3 < len){
44
+ const lineStart = p;
45
+
46
+ if (chunk[p] === 13 && chunk[p+1] === 10) {
47
+ p += 2;
48
+ break;
49
+ }
50
+
51
+ while(p < len && !(chunk[p] === 13 && chunk[p+1] === 10)) p++;
52
+
53
+ const h = chunk.subarray(lineStart, p);
54
+
55
+ switch((h[0] << 24) | (h[1] << 16) | (h[2] << 8) | h[3]){
56
+ case 0x436F6E74:{ // "Cont"
57
+ if(h[8] === 76 && h[14] === 58 && h[15] === 32){ // Content-Length:
58
+ length = parseInt(h.subarray(16));
59
+ break;
60
+ }else if(h[8] === 84 && h[12] === 58 && h[13] === 32){ // Content-Type:
61
+ type = h.subarray(14);
62
+ break;
63
+ }
64
+ }
65
+ case 0x486F7374:{ // "Host"
66
+ if(h[4] === 58 && h[5] === 32){
67
+ let colon = -1;
68
+ for(let i = h.length - 1; i > 5; i--){
69
+ if(h[i] === 58){
70
+ colon = i;
71
+ break;
72
+ }
73
+ }
74
+
75
+ if(colon !== -1){
76
+ host = h.subarray(6, colon).toString();
77
+ port = h.subarray(colon + 1).toString();
78
+ }else{
79
+ host = h.subarray(6).toString();
80
+ }
81
+ break;
82
+ }
83
+ }
84
+ case 0x436F6E6E:{ // "Conn"
85
+ // Connection
86
+ if(h[10] === 58 && h[11] === 32){
87
+ if(h[12] === 107 && h.length === 22){ // Connection: keep-alive
88
+ connection = 1
89
+ }else if(h[12] === 85 && h.length === 19){ // Connection: Upgrade
90
+ connection = 2
91
+ }
92
+ break;
93
+ }
94
+ }
95
+ default:{
96
+ header.push(h);
97
+ }
98
+ }
99
+
100
+ p += 2;
101
+ }
102
+
103
+ if(!host) return false;
104
+
105
+ const body = length ? chunk.subarray(p, p + length) : null;
106
+ if(length && body.length > length) return false;
107
+
108
+ return {
109
+ success:!length || body.length === length,
110
+ connection,
111
+ method,
112
+ host,
113
+ pathname: chunk.subarray(pathStart, searchPoint === -1 ? pathEnd : searchPoint),
114
+ search: searchPoint === -1 ? null : chunk.subarray(searchPoint + 1, pathEnd),
115
+ type,
116
+ length,
117
+ header,
118
+ payload: body ? [body] : []
119
+ };
120
+ }
@@ -0,0 +1,47 @@
1
+ module.exports = function checkPath(tree, req) {
2
+ let hostTree = tree[req.host] || tree['*'];
3
+ if (!hostTree) return false;
4
+
5
+ let node = hostTree[req.connection === 2 ? 'websocket' : req.method] || hostTree['*'];
6
+ if (!node) return false;
7
+
8
+ const path = req.pathname;
9
+ let start = 1;
10
+ req.params = {};
11
+ req.wildcard = {};
12
+
13
+ let cacheWildcard = false;
14
+ while (true) {
15
+
16
+ if(node.wildcard){
17
+ cacheWildcard = {
18
+ wildcard: path.slice(start - 1),
19
+ node:node.wildcard
20
+ }
21
+ }
22
+
23
+ let end = path.indexOf('/', start);
24
+ if (end === -1) end = path.length;
25
+
26
+ const part = path.slice(start, end);
27
+
28
+ if(node.static && node.static[part]){
29
+ node = node.static[part];
30
+ }else if (node.param){
31
+ req.params[node.param.id] = part;
32
+ node = node.param;
33
+ }else if(cacheWildcard !== false){
34
+ req.wildcard = cacheWildcard.wildcard;
35
+ return cacheWildcard.node.handler;
36
+ }else{
37
+ return false;
38
+ }
39
+
40
+ if (end === path.length) break;
41
+ start = end + 1;
42
+ }
43
+
44
+ if (!node.handler) return false;
45
+
46
+ return node.handler;
47
+ }
@@ -0,0 +1,161 @@
1
+ function isValidDecimal(s) {
2
+ for (let i = 0; i < s.length; i++) {
3
+ const c = s.charCodeAt(i);
4
+
5
+ if (
6
+ (c >= 48 && c <= 57) // 0-9
7
+ ) continue;
8
+
9
+ return false;
10
+ }
11
+ return s.length > 0;
12
+ }
13
+ function isValidHex(s) {
14
+ for (let i = 0; i < s.length; i++) {
15
+ const c = s.charCodeAt(i);
16
+
17
+ if (
18
+ (c >= 48 && c <= 57) || // 0-9
19
+ (c >= 97 && c <= 102) // a-f
20
+ ) continue;
21
+
22
+ return false;
23
+ }
24
+ return s.length > 0;
25
+ }
26
+ function isValidUser(s) {
27
+ for (let i = 0; i < s.length; i++) {
28
+ const c = s.charCodeAt(i);
29
+
30
+ if (
31
+ (c >= 48 && c <= 57) || // 0-9
32
+ (c >= 65 && c <= 90) || // A-Z
33
+ (c >= 97 && c <= 122) || // a-z
34
+ c === 95 || // _
35
+ c === 45 // -
36
+ ) continue;
37
+
38
+ return false;
39
+ }
40
+ return s.length > 0;
41
+ }
42
+ function isValidEmail(s){
43
+ let at = -1;
44
+ let dot = -1;
45
+
46
+ for (let i = 0; i < s.length; i++) {
47
+ const c = s.charCodeAt(i);
48
+
49
+ if (c === 64) { // @
50
+ if (at !== -1) return false;
51
+ at = i;
52
+ continue;
53
+ }
54
+
55
+ if (c === 46) dot = i; // .
56
+
57
+ if (
58
+ (c >= 48 && c <= 57) || // 0-9
59
+ (c >= 65 && c <= 90) || // A-Z
60
+ (c >= 97 && c <= 122) || // a-z
61
+ c === 46 || c === 95 || // . _
62
+ c === 45 || c === 43 || // - +
63
+ c === 37 // %
64
+ ) continue;
65
+
66
+ return false;
67
+ }
68
+
69
+ return at > 0 && dot > at + 1 && dot < s.length - 1;
70
+ }
71
+
72
+ module.exports = function({type, length, format, list, mime}, data){
73
+ let status = false;
74
+ let len = false;
75
+
76
+ if(type == 'int' && data === (data | 0)){
77
+ len = data;
78
+ status = true;
79
+ }else if(type == 'string' && typeof data === 'string'){
80
+ len = data.length;
81
+ status = true;
82
+ }else if(type == 'boolean' && typeof data === 'boolean'){
83
+ status = true;
84
+ }else if(type == 'array' && typeof data === 'object' && Array.isArray(data)){
85
+ status = true;
86
+ }else if(type == 'object' && typeof data === 'object' && !Array.isArray(data)){
87
+ status = true;
88
+ }else if(type == 'file' && typeof data === 'object' && Buffer.isBuffer(data?.data)){
89
+ len = data.data.length;
90
+ status = true;
91
+ }else if(type == 'timestamp' && typeof data === 'string' && data.length == 24 && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(data)){
92
+ len = new Date(data).getTime();
93
+ status = true;
94
+ }
95
+
96
+ if(status === false){
97
+ return false;
98
+ }
99
+
100
+ if(mime !== undefined){
101
+ status = false;
102
+ if(mime.includes(data.type)){
103
+ status = true;
104
+ }
105
+
106
+ if(status === false){
107
+ return false;
108
+ }
109
+ }
110
+
111
+ if(length !== undefined){
112
+ status = false;
113
+ if(Array.isArray(length) && length.length == 2){
114
+ if(length[0] <= len && length[1] >= len){
115
+ status = true;
116
+ }
117
+ }else if(length === (length | 0)){
118
+ if(len === length){
119
+ status = true;
120
+ }
121
+ }
122
+
123
+ if(status === false){
124
+ return false;
125
+ }
126
+ }
127
+
128
+ if(format !== undefined){
129
+ status = false;
130
+ if(format == 'user'){
131
+ if(isValidUser(data)){
132
+ status = true;
133
+ }
134
+ }else if(format == 'email'){
135
+ if(isValidEmail(data)){
136
+ status = true;
137
+ }
138
+ }else if(format == 'hex'){
139
+ if(isValidHex(data)){
140
+ status = true;
141
+ }
142
+ }else if(format == 'decimal'){
143
+ if(isValidDecimal(data)){
144
+ status = true;
145
+ }
146
+ }
147
+
148
+ if(status === false){
149
+ return false;
150
+ }
151
+ }
152
+
153
+ if(Array.isArray(list)){
154
+ if(!list.includes(data)){
155
+ return false;
156
+ }
157
+ }
158
+
159
+
160
+ return true;
161
+ }
@@ -0,0 +1,15 @@
1
+ module.exports = function(cookie){
2
+ let output = [];
3
+
4
+ for(let i of cookie.split('; ')){
5
+ let index = i.indexOf('=');
6
+
7
+ try{
8
+ output[decodeURIComponent(i.slice(0, index))] = decodeURIComponent(i.slice(index + 1));
9
+ }catch(e){
10
+ console.log(e);
11
+ }
12
+ }
13
+
14
+ return output;
15
+ }