@sqb/oracle-dialect 4.1.5 → 4.5.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.
- package/README.md +67 -0
- package/cjs/index.js +5 -0
- package/{dist/OracleSerializer.js → cjs/oracle-serializer.js} +38 -12
- package/cjs/package.json +3 -0
- package/{dist → esm}/index.d.ts +0 -0
- package/esm/index.js +3 -0
- package/{dist/OracleSerializer.d.ts → esm/oracle-serializer.d.ts} +2 -0
- package/esm/oracle-serializer.js +124 -0
- package/package.json +47 -38
- package/dist/index.js +0 -5
package/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
|
|
2
|
+
<p style="text-align:center">
|
|
3
|
+
<img src="https://user-images.githubusercontent.com/3836517/32965280-1a2b63ce-cbe7-11e7-8ee1-ba47313503c5.png" width="500px" alt="SQB Logo"/>
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
<br>
|
|
7
|
+
|
|
8
|
+
[![NPM Version][npm-image]][npm-url]
|
|
9
|
+
[![NPM Downloads][downloads-image]][downloads-url]
|
|
10
|
+
[![Build Status][travis-image]][travis-url]
|
|
11
|
+
[![Test Coverage][coveralls-image]][coveralls-url]
|
|
12
|
+
[![Dependencies][dependencies-image]][dependencies-url]
|
|
13
|
+
[![DevDependencies][devdependencies-image]][devdependencies-url]
|
|
14
|
+
[![Package Quality][quality-image]][quality-url]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## About SQB
|
|
18
|
+
|
|
19
|
+
SQB is an extensible, multi-dialect SQL query builder and Database connection wrapper for NodeJS.
|
|
20
|
+
|
|
21
|
+
## Main goals
|
|
22
|
+
|
|
23
|
+
- Single code base for any sql based database
|
|
24
|
+
- Powerful and simplified query coding scheme
|
|
25
|
+
- Fast applications with low memory requirements
|
|
26
|
+
- Let applications work with large data tables efficiently
|
|
27
|
+
- Support latest JavaScript language standards
|
|
28
|
+
- Lightweight and extensible framework.
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
You can report bugs and discuss features on the [GitHub issues](https://github.com/sqbjs/sqb/issues) page
|
|
32
|
+
|
|
33
|
+
Thanks to all of the great [contributions](https://github.com/sqbjs/sqb/graphs/contributors) to the project.
|
|
34
|
+
|
|
35
|
+
You may want to check detailed [DOCUMENTATION](https://sqbjs.github.io/sqb/)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
$ npm install @sqb/oracle-dialect --save
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Node Compatibility
|
|
45
|
+
|
|
46
|
+
- node >= 16.x
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
### License
|
|
50
|
+
SQB is available under [MIT](LICENSE) license.
|
|
51
|
+
|
|
52
|
+
[npm-image]: https://img.shields.io/npm/v/@sqb/oracle-dialect.svg
|
|
53
|
+
[npm-url]: https://npmjs.org/package/@sqb/oracle-dialect
|
|
54
|
+
[travis-image]: https://img.shields.io/travis/sqbjs/@sqb/oracle-dialect/master.svg
|
|
55
|
+
[travis-url]: https://travis-ci.org/sqbjs/@sqb/oracle-dialect
|
|
56
|
+
[coveralls-image]: https://img.shields.io/coveralls/sqbjs/@sqb/oracle-dialect/master.svg
|
|
57
|
+
[coveralls-url]: https://coveralls.io/r/sqbjs/@sqb/oracle-dialect
|
|
58
|
+
[downloads-image]: https://img.shields.io/npm/dm/@sqb/oracle-dialect.svg
|
|
59
|
+
[downloads-url]: https://npmjs.org/package/@sqb/oracle-dialect
|
|
60
|
+
[gitter-image]: https://badges.gitter.im/sqbjs/@sqb/oracle-dialect.svg
|
|
61
|
+
[gitter-url]: https://gitter.im/sqbjs/@sqb/oracle-dialect?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
|
62
|
+
[dependencies-image]: https://david-dm.org/sqbjs/@sqb/oracle-dialect/status.svg
|
|
63
|
+
[dependencies-url]:https://david-dm.org/sqbjs/@sqb/oracle-dialect
|
|
64
|
+
[devdependencies-image]: https://david-dm.org/sqbjs/@sqb/oracle-dialect/dev-status.svg
|
|
65
|
+
[devdependencies-url]:https://david-dm.org/sqbjs/@sqb/oracle-dialect?type=dev
|
|
66
|
+
[quality-image]: http://npm.packagequality.com/shield/@sqb/oracle-dialect.png
|
|
67
|
+
[quality-url]: http://packagequality.com/#?package=@sqb/oracle-dialect
|
package/cjs/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const builder_1 = require("@sqb/builder");
|
|
4
|
+
const oracle_serializer_js_1 = require("./oracle-serializer.js");
|
|
5
|
+
(0, builder_1.registerSerializer)(new oracle_serializer_js_1.OracleSerializer());
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OracleSerializer = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const compareVersion = tslib_1.__importStar(require("compare-versions"));
|
|
6
4
|
const builder_1 = require("@sqb/builder");
|
|
7
5
|
const reservedWords = ['comment', 'dual'];
|
|
8
6
|
class OracleSerializer {
|
|
9
|
-
|
|
10
|
-
this.dialect = 'oracle';
|
|
11
|
-
}
|
|
7
|
+
dialect = 'oracle';
|
|
12
8
|
isReservedWord(ctx, s) {
|
|
13
9
|
return s && typeof s === 'string' &&
|
|
14
10
|
reservedWords.includes(s.toLowerCase());
|
|
@@ -29,6 +25,10 @@ class OracleSerializer {
|
|
|
29
25
|
return this._serializeBooleanValue(ctx, o);
|
|
30
26
|
case builder_1.SerializationType.RETURNING_BLOCK:
|
|
31
27
|
return this._serializeReturning();
|
|
28
|
+
case builder_1.SerializationType.STRINGAGG_STATEMENT:
|
|
29
|
+
return this._serializeStringAGG(ctx, o, defFn);
|
|
30
|
+
case builder_1.SerializationType.SEQUENCE_GETTER_STATEMENT:
|
|
31
|
+
return this._serializeSequenceGetter(ctx, o, defFn);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
_serializeSelect(ctx, o, defFn) {
|
|
@@ -36,7 +36,7 @@ class OracleSerializer {
|
|
|
36
36
|
const limit = o.limit || 0;
|
|
37
37
|
const offset = Math.max((o.offset || 0), 0);
|
|
38
38
|
if (limit || offset) {
|
|
39
|
-
if (ctx.dialectVersion &&
|
|
39
|
+
if (ctx.dialectVersion && ctx.dialectVersion >= '12') {
|
|
40
40
|
if (offset)
|
|
41
41
|
out += '\nOFFSET ' + offset + ' ROWS' +
|
|
42
42
|
(limit ? ' FETCH NEXT ' + limit + ' ROWS ONLY' : '');
|
|
@@ -67,13 +67,22 @@ class OracleSerializer {
|
|
|
67
67
|
return defFn(ctx, o) || 'from dual';
|
|
68
68
|
}
|
|
69
69
|
_serializeComparison(ctx, o, defFn) {
|
|
70
|
-
if (o.right)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
if ((o.right.expression && o.right.expression === 'null') ||
|
|
71
|
+
((!o.right.expression || o.right.expression.startsWith(':')) && o.right.value == null)) {
|
|
72
|
+
if (o.right.expression?.startsWith(':')) {
|
|
73
|
+
if (ctx.params)
|
|
74
|
+
delete ctx.params[o.right.expression.substring(1)];
|
|
75
|
+
if (ctx.preparedParams)
|
|
76
|
+
delete ctx.preparedParams[o.right.expression.substring(1)];
|
|
77
|
+
if (ctx.paramOptions)
|
|
78
|
+
delete ctx.paramOptions[o.right.expression.substring(1)];
|
|
79
|
+
o.right.expression = 'null';
|
|
80
|
+
o.right.isParam = false;
|
|
76
81
|
}
|
|
82
|
+
if (o.operatorType === 'eq')
|
|
83
|
+
return defFn(ctx, { ...o, operatorType: builder_1.OperatorType.is, symbol: 'is' });
|
|
84
|
+
if (o.operatorType === 'ne')
|
|
85
|
+
return defFn(ctx, { ...o, operatorType: builder_1.OperatorType.isNot, symbol: 'is not' });
|
|
77
86
|
}
|
|
78
87
|
return defFn(ctx, o);
|
|
79
88
|
}
|
|
@@ -95,6 +104,23 @@ class OracleSerializer {
|
|
|
95
104
|
_serializeBooleanValue(_ctx, o) {
|
|
96
105
|
return o == null ? 'null' : (o ? '1' : '0');
|
|
97
106
|
}
|
|
107
|
+
// noinspection JSUnusedLocalSymbols
|
|
108
|
+
_serializeStringAGG(ctx, o,
|
|
109
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
110
|
+
defFn) {
|
|
111
|
+
return 'listagg(' + o.field +
|
|
112
|
+
',\'' + o.delimiter + '\') within group (' +
|
|
113
|
+
(o.orderBy ? o.orderBy : 'order by null') + ')' +
|
|
114
|
+
(o.alias ? ' ' + o.alias : '');
|
|
115
|
+
}
|
|
116
|
+
// noinspection JSUnusedLocalSymbols
|
|
117
|
+
_serializeSequenceGetter(ctx, o,
|
|
118
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
119
|
+
defFn) {
|
|
120
|
+
return o.genName + '.' +
|
|
121
|
+
(o.next ? 'nextval' : 'currval') +
|
|
122
|
+
(o.alias ? ' ' + o.alias : '');
|
|
123
|
+
}
|
|
98
124
|
_serializeReturning() {
|
|
99
125
|
return '';
|
|
100
126
|
}
|
package/cjs/package.json
ADDED
package/{dist → esm}/index.d.ts
RENAMED
|
File without changes
|
package/esm/index.js
ADDED
|
@@ -9,5 +9,7 @@ export declare class OracleSerializer implements SerializerExtension {
|
|
|
9
9
|
private _serializeStringValue;
|
|
10
10
|
private _serializeDateValue;
|
|
11
11
|
private _serializeBooleanValue;
|
|
12
|
+
private _serializeStringAGG;
|
|
13
|
+
private _serializeSequenceGetter;
|
|
12
14
|
private _serializeReturning;
|
|
13
15
|
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { OperatorType, SerializationType } from '@sqb/builder';
|
|
2
|
+
const reservedWords = ['comment', 'dual'];
|
|
3
|
+
export class OracleSerializer {
|
|
4
|
+
dialect = 'oracle';
|
|
5
|
+
isReservedWord(ctx, s) {
|
|
6
|
+
return s && typeof s === 'string' &&
|
|
7
|
+
reservedWords.includes(s.toLowerCase());
|
|
8
|
+
}
|
|
9
|
+
serialize(ctx, type, o, defFn) {
|
|
10
|
+
switch (type) {
|
|
11
|
+
case SerializationType.SELECT_QUERY:
|
|
12
|
+
return this._serializeSelect(ctx, o, defFn);
|
|
13
|
+
case SerializationType.SELECT_QUERY_FROM:
|
|
14
|
+
return this._serializeFrom(ctx, o, defFn);
|
|
15
|
+
case SerializationType.COMPARISON_EXPRESSION:
|
|
16
|
+
return this._serializeComparison(ctx, o, defFn);
|
|
17
|
+
case SerializationType.STRING_VALUE:
|
|
18
|
+
return this._serializeStringValue(ctx, o, defFn);
|
|
19
|
+
case SerializationType.DATE_VALUE:
|
|
20
|
+
return this._serializeDateValue(ctx, o, defFn);
|
|
21
|
+
case SerializationType.BOOLEAN_VALUE:
|
|
22
|
+
return this._serializeBooleanValue(ctx, o);
|
|
23
|
+
case SerializationType.RETURNING_BLOCK:
|
|
24
|
+
return this._serializeReturning();
|
|
25
|
+
case SerializationType.STRINGAGG_STATEMENT:
|
|
26
|
+
return this._serializeStringAGG(ctx, o, defFn);
|
|
27
|
+
case SerializationType.SEQUENCE_GETTER_STATEMENT:
|
|
28
|
+
return this._serializeSequenceGetter(ctx, o, defFn);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
_serializeSelect(ctx, o, defFn) {
|
|
32
|
+
let out = defFn(ctx, o);
|
|
33
|
+
const limit = o.limit || 0;
|
|
34
|
+
const offset = Math.max((o.offset || 0), 0);
|
|
35
|
+
if (limit || offset) {
|
|
36
|
+
if (ctx.dialectVersion && ctx.dialectVersion >= '12') {
|
|
37
|
+
if (offset)
|
|
38
|
+
out += '\nOFFSET ' + offset + ' ROWS' +
|
|
39
|
+
(limit ? ' FETCH NEXT ' + limit + ' ROWS ONLY' : '');
|
|
40
|
+
else
|
|
41
|
+
out += '\nFETCH FIRST ' + limit + ' ROWS ONLY';
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
if (offset || o.orderBy) {
|
|
45
|
+
out = 'select * from (\n\t' +
|
|
46
|
+
'select /*+ first_rows(' + (limit || 100) +
|
|
47
|
+
') */ t.*, rownum row$number from (\n\t' +
|
|
48
|
+
out + '\n\b' +
|
|
49
|
+
') t' +
|
|
50
|
+
(limit ? ' where rownum <= ' + (limit + offset) : '') + '\n\b)';
|
|
51
|
+
if (offset)
|
|
52
|
+
out += ' where row$number >= ' + (offset + 1);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
out = 'select * from (\n\t' +
|
|
56
|
+
out + '\n\b' +
|
|
57
|
+
') where rownum <= ' + limit;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
_serializeFrom(ctx, o, defFn) {
|
|
64
|
+
return defFn(ctx, o) || 'from dual';
|
|
65
|
+
}
|
|
66
|
+
_serializeComparison(ctx, o, defFn) {
|
|
67
|
+
if ((o.right.expression && o.right.expression === 'null') ||
|
|
68
|
+
((!o.right.expression || o.right.expression.startsWith(':')) && o.right.value == null)) {
|
|
69
|
+
if (o.right.expression?.startsWith(':')) {
|
|
70
|
+
if (ctx.params)
|
|
71
|
+
delete ctx.params[o.right.expression.substring(1)];
|
|
72
|
+
if (ctx.preparedParams)
|
|
73
|
+
delete ctx.preparedParams[o.right.expression.substring(1)];
|
|
74
|
+
if (ctx.paramOptions)
|
|
75
|
+
delete ctx.paramOptions[o.right.expression.substring(1)];
|
|
76
|
+
o.right.expression = 'null';
|
|
77
|
+
o.right.isParam = false;
|
|
78
|
+
}
|
|
79
|
+
if (o.operatorType === 'eq')
|
|
80
|
+
return defFn(ctx, { ...o, operatorType: OperatorType.is, symbol: 'is' });
|
|
81
|
+
if (o.operatorType === 'ne')
|
|
82
|
+
return defFn(ctx, { ...o, operatorType: OperatorType.isNot, symbol: 'is not' });
|
|
83
|
+
}
|
|
84
|
+
return defFn(ctx, o);
|
|
85
|
+
}
|
|
86
|
+
_serializeStringValue(ctx, o, defFn) {
|
|
87
|
+
if (typeof o === 'string') {
|
|
88
|
+
if (o.match(/^\d{4}-\d{2}-\d{2}$/))
|
|
89
|
+
return 'to_date(' + o + ', \'yyyy-mm-dd\')';
|
|
90
|
+
if (o.match(/^\d{4}-\d{2}-\d{2}T/))
|
|
91
|
+
return `to_timestamp_tz('${o}','yyyy-mm-dd"T"hh24:mi:sstzh:tzm')`;
|
|
92
|
+
}
|
|
93
|
+
return defFn(ctx, o);
|
|
94
|
+
}
|
|
95
|
+
_serializeDateValue(ctx, o, defFn) {
|
|
96
|
+
const s = defFn(ctx, o);
|
|
97
|
+
return s && (s.length <= 12 ?
|
|
98
|
+
'to_date(' + s + ', \'yyyy-mm-dd\')' :
|
|
99
|
+
'to_date(' + s + ', \'yyyy-mm-dd hh24:mi:ss\')');
|
|
100
|
+
}
|
|
101
|
+
_serializeBooleanValue(_ctx, o) {
|
|
102
|
+
return o == null ? 'null' : (o ? '1' : '0');
|
|
103
|
+
}
|
|
104
|
+
// noinspection JSUnusedLocalSymbols
|
|
105
|
+
_serializeStringAGG(ctx, o,
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
107
|
+
defFn) {
|
|
108
|
+
return 'listagg(' + o.field +
|
|
109
|
+
',\'' + o.delimiter + '\') within group (' +
|
|
110
|
+
(o.orderBy ? o.orderBy : 'order by null') + ')' +
|
|
111
|
+
(o.alias ? ' ' + o.alias : '');
|
|
112
|
+
}
|
|
113
|
+
// noinspection JSUnusedLocalSymbols
|
|
114
|
+
_serializeSequenceGetter(ctx, o,
|
|
115
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
116
|
+
defFn) {
|
|
117
|
+
return o.genName + '.' +
|
|
118
|
+
(o.next ? 'nextval' : 'currval') +
|
|
119
|
+
(o.alias ? ' ' + o.alias : '');
|
|
120
|
+
}
|
|
121
|
+
_serializeReturning() {
|
|
122
|
+
return '';
|
|
123
|
+
}
|
|
124
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sqb/oracle-dialect",
|
|
3
3
|
"description": "SQB serialization extension for Oracle database",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.5.0",
|
|
5
5
|
"author": "Panates",
|
|
6
6
|
"contributors": [
|
|
7
|
-
"Eray Hanoglu <e.hanoglu@panates.com>"
|
|
7
|
+
"Eray Hanoglu <e.hanoglu@panates.com>",
|
|
8
|
+
"Ilker Gurelli <i.gurelli@panates.com>"
|
|
8
9
|
],
|
|
9
10
|
"license": "Apache-2.0",
|
|
10
11
|
"repository": {
|
|
@@ -12,47 +13,55 @@
|
|
|
12
13
|
"url": "https://github.com/sqbjs/sqb.git",
|
|
13
14
|
"directory": "packages/oracle-dialect"
|
|
14
15
|
},
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"dialect",
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"fast-glob": "^3.2.11"
|
|
16
|
+
"scripts": {
|
|
17
|
+
"compile": "tsc",
|
|
18
|
+
"prebuild": "npm run lint && npm run clean",
|
|
19
|
+
"build": "npm run build:cjs && npm run build:esm",
|
|
20
|
+
"build:cjs": "tsc -b tsconfig-build-cjs.json",
|
|
21
|
+
"build:esm": "tsc -b tsconfig-build-esm.json",
|
|
22
|
+
"postbuild": "cp README.md package.json ../../LICENSE ../../build/oracle-dialect && cp ../../package.cjs.json ../../build/oracle-dialect/cjs/package.json",
|
|
23
|
+
"lint": "eslint .",
|
|
24
|
+
"test": "jest",
|
|
25
|
+
"cover": "jest --collect-coverage",
|
|
26
|
+
"clean": "npm run clean:src | npm run clean:dist | npm run clean:cover",
|
|
27
|
+
"clean:src": "ts-cleanup -s src --all",
|
|
28
|
+
"clean:dist": "rimraf ../../build/oracle-dialect",
|
|
29
|
+
"clean:cover": "rimraf ../../coverage/oracle-dialect"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"@sqb/builder": "^4.
|
|
32
|
+
"@sqb/builder": "^4.5.0"
|
|
33
33
|
},
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
|
|
34
|
+
"type": "module",
|
|
35
|
+
"main": "cjs/index.js",
|
|
36
|
+
"module": "esm/index.js",
|
|
37
|
+
"types": "esm/index.d.ts",
|
|
38
|
+
"exports": {
|
|
39
|
+
".": {
|
|
40
|
+
"require": "./cjs/index.js",
|
|
41
|
+
"default": "./esm/index.js"
|
|
42
|
+
},
|
|
43
|
+
"./cjs": "./cjs/index.js",
|
|
44
|
+
"./esm": "./esm/index.js"
|
|
38
45
|
},
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=16.0",
|
|
48
|
+
"npm": ">=7.0.0"
|
|
42
49
|
},
|
|
43
50
|
"files": [
|
|
51
|
+
"bin/",
|
|
52
|
+
"cjs/",
|
|
53
|
+
"esm/",
|
|
44
54
|
"LICENSE",
|
|
45
|
-
"README"
|
|
46
|
-
"dist/"
|
|
55
|
+
"README.md"
|
|
47
56
|
],
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
}
|
|
57
|
+
"keywords": [
|
|
58
|
+
"sqb",
|
|
59
|
+
"oracle",
|
|
60
|
+
"sql",
|
|
61
|
+
"query",
|
|
62
|
+
"builder",
|
|
63
|
+
"dialect",
|
|
64
|
+
"serializer",
|
|
65
|
+
"database"
|
|
66
|
+
]
|
|
67
|
+
}
|
package/dist/index.js
DELETED