@ellipticltd/aml-utils 0.1.1-ts.3 → 0.1.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/.npmignore ADDED
@@ -0,0 +1,4 @@
1
+ **/*.coffee
2
+ test/
3
+ .history
4
+ .vscode
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ declare module '@ellipticltd/aml-utils';
package/index.js ADDED
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ types: require('./lib/types'),
3
+ validations: require('./lib/validations'),
4
+ errors: require('./lib/errors'),
5
+ formatting: require('./lib/formatting'),
6
+ middleware: require('./lib/middleware'),
7
+ ormHelpers: require('./lib/ormHelpers'),
8
+ };
package/lib/errors.js ADDED
@@ -0,0 +1,53 @@
1
+ const create = require('create-error');
2
+
3
+ const AppError = create('AppError');
4
+
5
+ const RequestError = create(AppError, 'RequestError');
6
+
7
+ RequestError.toJSON = () => ({
8
+ message: this.message,
9
+ });
10
+
11
+ const Forbidden = create(RequestError, 'ForbiddenError', {
12
+ status: 403,
13
+ });
14
+
15
+ const Unauthorized = create(RequestError, 'UnauthorizedError', {
16
+ status: 401,
17
+ });
18
+
19
+ const BadRequest = create(RequestError, 'BadRequestError', {
20
+ status: 400,
21
+ });
22
+
23
+ const NotFound = create(RequestError, 'NotFoundError', {
24
+ status: 404,
25
+ });
26
+
27
+ const ConflictError = create(RequestError, 'ConflictError', {
28
+ status: 409,
29
+ });
30
+
31
+ const ServerError = create(RequestError, 'ServerError', {
32
+ status: 500,
33
+ });
34
+
35
+ const InvalidArguments = create(RequestError, 'InvalidArgumentsError', {
36
+ status: 500,
37
+ });
38
+
39
+ const ServerTimeout = create(RequestError, 'ServerTimeoutError', {
40
+ status: 503,
41
+ });
42
+
43
+ module.exports = {
44
+ RequestError,
45
+ ServerError,
46
+ Forbidden,
47
+ Unauthorized,
48
+ BadRequest,
49
+ NotFound,
50
+ ConflictError,
51
+ InvalidArguments,
52
+ ServerTimeout,
53
+ };
@@ -0,0 +1,18 @@
1
+ const _ = require('lodash');
2
+
3
+ const rethrowError = (fnName, e) => {
4
+ let formattedError;
5
+ if (_.isPlainObject(e.error) || _.isArray(e.error)) {
6
+ formattedError = `\n${JSON.stringify(e.error, null, 2)}`;
7
+ } else {
8
+ formattedError = e.error;
9
+ }
10
+ return `${fnName} - ${e.name}: ${e.statusCode} - ${formattedError}`;
11
+ };
12
+
13
+ const sqlEscapeWildcard = str => str.replace(/_|%|\\/g, x => `\\${x}`);
14
+
15
+ module.exports = {
16
+ rethrowError,
17
+ sqlEscapeWildcard,
18
+ };
@@ -0,0 +1,25 @@
1
+ const T = require('./types');
2
+
3
+ const ensureType = type => (req, res, next, id, name) => {
4
+ let error;
5
+ try {
6
+ T.ensureType(type, id, `Invalid ${name}`);
7
+ return next();
8
+ } catch (error1) {
9
+ error = error1;
10
+ return next(error);
11
+ }
12
+ };
13
+
14
+ const ensureUUID = ensureType('UUID');
15
+
16
+ const ensureHex32 = ensureType('Hex32');
17
+
18
+ const ensureInteger = ensureType('IntString');
19
+
20
+ module.exports = {
21
+ ensureType,
22
+ ensureUUID,
23
+ ensureHex32,
24
+ ensureInteger,
25
+ };
@@ -0,0 +1,19 @@
1
+ const _ = require('lodash');
2
+
3
+ const includeNested = (orm, models) =>
4
+ _.map(_.toPairs(models), (arg) => {
5
+ const [modelName, inclusion] = arg;
6
+ const include = includeNested(orm, inclusion);
7
+ const model = orm[modelName];
8
+ if (_.isEmpty(include)) {
9
+ return model;
10
+ }
11
+ return {
12
+ model,
13
+ include,
14
+ };
15
+ });
16
+
17
+ module.exports = {
18
+ includeNested,
19
+ };
package/lib/types.js ADDED
@@ -0,0 +1,211 @@
1
+ const TC = require('type-check');
2
+
3
+ const E = require('./errors');
4
+
5
+ const V = require('./validations');
6
+
7
+ const _ = require('lodash');
8
+
9
+ const customTypes = {
10
+ Integer: {
11
+ typeOf: 'Number',
12
+ validate(x) {
13
+ return x % 1 === 0;
14
+ },
15
+ },
16
+ IntString: {
17
+ typeOf: 'String',
18
+ validate: V.isInt,
19
+ },
20
+ BoolString: {
21
+ typeOf: 'String',
22
+ validate: V.isBoolean,
23
+ },
24
+ Hex32: {
25
+ typeOf: 'String',
26
+ validate(x) {
27
+ return V.isHexadecimal(x) && x.length === 32;
28
+ },
29
+ },
30
+ Email: {
31
+ typeOf: 'String',
32
+ validate: V.isEmail,
33
+ },
34
+ UUID: {
35
+ typeOf: 'String',
36
+ validate: V.isUUID,
37
+ },
38
+ UUIDv4: {
39
+ typeOf: 'String',
40
+ validate(x) {
41
+ return V.isUUID(x, 4);
42
+ },
43
+ },
44
+ JSON: {
45
+ typeOf: 'String',
46
+ validate: V.isJSON,
47
+ },
48
+ Url: {
49
+ typeOf: 'String',
50
+ validate: V.isURL,
51
+ },
52
+ DateString: {
53
+ typeOf: 'String',
54
+ validate: V.isDate,
55
+ },
56
+ NonEmptyArray: {
57
+ typeOf: 'Array',
58
+ validate: V.nonEmpty,
59
+ },
60
+ NonNullString: {
61
+ typeOf: 'String',
62
+ validate: V.nonEmpty,
63
+ },
64
+ CustomerReference: {
65
+ typeOf: 'String',
66
+ validate: V.isCustomerReference,
67
+ },
68
+ CustomerLabelName: {
69
+ typeOf: 'String',
70
+ validate: V.isCustomerLabelName,
71
+ },
72
+ BitcoinAddress: {
73
+ typeOf: 'String',
74
+ validate: V.bitcoin.isAddress,
75
+ },
76
+ EthereumAddress: {
77
+ typeOf: 'String',
78
+ validate: V.ethereum.isAddress,
79
+ },
80
+ EthereumTx: {
81
+ typeOf: 'String',
82
+ validate: V.ethereum.isTxHash,
83
+ },
84
+ EthereumBlockHash: {
85
+ typeOf: 'String',
86
+ validate: V.ethereum.isBlockHash,
87
+ },
88
+ EthereumWeiAmount: {
89
+ typeOf: 'String',
90
+ validate: V.ethereum.isValidWeiAmount,
91
+ },
92
+ EthereumAddressCode: {
93
+ typeOf: 'String',
94
+ validate: V.ethereum.isAddressCode,
95
+ },
96
+ MineId: {
97
+ typeOf: 'String',
98
+ validate(x) {
99
+ return x === 'mine';
100
+ },
101
+ },
102
+ BitcoinTxHash: {
103
+ typeOf: 'String',
104
+ validate: V.bitcoin.isTxHash,
105
+ },
106
+ BitcoinAddressArray: {
107
+ typeOf: 'Array',
108
+ validate(as) {
109
+ return _.every(as, V.bitcoin.isAddress);
110
+ },
111
+ },
112
+ BitcoinTxHashArray: {
113
+ typeOf: 'Array',
114
+ validate(as) {
115
+ return _.every(as, V.bitcoin.isTxHash);
116
+ },
117
+ },
118
+ BitcoinTxHex: {
119
+ typeOf: 'String',
120
+ validate: V.bitcoin.isTxHex,
121
+ },
122
+ BitcoinScriptHex: {
123
+ typeOf: 'String',
124
+ validate: V.bitcoin.isScriptHex,
125
+ },
126
+ BitcoinHDPath: {
127
+ typeOf: 'String',
128
+ validate: V.bitcoin.isHDPath,
129
+ },
130
+ BitcoinPublicKey: {
131
+ typeOf: 'String',
132
+ validate: V.bitcoin.isPublicKey,
133
+ },
134
+ BitcoinXpub: {
135
+ typeOf: 'String',
136
+ validate: V.bitcoin.isHDPublicKey,
137
+ },
138
+ BitcoinTxSignature: {
139
+ typeOf: 'String',
140
+ validate: V.bitcoin.isTxSignature,
141
+ },
142
+ BitcoinBlockHeight: {
143
+ typeOf: 'Number',
144
+ validate: V.bitcoin.isBlockHeight,
145
+ },
146
+ };
147
+
148
+ // add support for nullable properties
149
+ const withNullable = Object.keys(customTypes).reduce((acc, _type) => {
150
+ acc[_type] = customTypes[_type];
151
+ const { typeOf, validate } = customTypes[_type];
152
+
153
+ // for every type, add Nullable+type. A nullable string can also be empty
154
+ acc[`Nullable${_type}`] = Object.assign({}, customTypes[_type], {
155
+ validate: x =>
156
+ x == null || (typeOf === 'String' && x === '') || validate(x),
157
+ });
158
+
159
+ return acc;
160
+ }, {});
161
+
162
+ module.exports = {
163
+ parseType(str) {
164
+ return TC.parseType(str);
165
+ },
166
+ parsedTypeCheck(type, obj) {
167
+ return TC.parsedTypeCheck(type, obj, this.opts);
168
+ },
169
+ typeCheck(type, obj) {
170
+ return TC.typeCheck(type, obj, this.opts);
171
+ },
172
+ opts: {
173
+ customTypes: withNullable,
174
+ },
175
+ ensureType(type, obj, msg) {
176
+ return V.ensure(this.typeCheck(type, obj), msg);
177
+ },
178
+ checkArg(obj, prop, type, msg) {
179
+ if (msg == null) {
180
+ msg = `Invalid ${prop}`;
181
+ }
182
+ this.ensureType(type, obj[prop], msg);
183
+ return obj[prop];
184
+ },
185
+ checkArgs(input, types) {
186
+ const inputKeys = {};
187
+ const [typ] = types;
188
+ const fields = typ.of;
189
+ let numInputKeys = 0;
190
+ let numKeys;
191
+
192
+ Object.keys(input).forEach((k) => {
193
+ inputKeys[k] = true;
194
+ numInputKeys += 1;
195
+ });
196
+
197
+ numKeys = 0;
198
+
199
+ Object.keys(fields).forEach((key) => {
200
+ types = fields[key];
201
+ V.ensure(this.parsedTypeCheck(types, input[key]), `Invalid ${key}`);
202
+ if (inputKeys[key]) {
203
+ numKeys += 1;
204
+ }
205
+ });
206
+
207
+ if (!(typ.subset || numInputKeys === numKeys)) {
208
+ throw new E.BadRequest('invalid extra arguments are present');
209
+ }
210
+ },
211
+ };
@@ -0,0 +1,236 @@
1
+ const E = require('./errors');
2
+ const V = require('validator');
3
+ const _ = require('lodash');
4
+ const {
5
+ crypto: { Signature },
6
+ HDPrivateKey,
7
+ HDPublicKey,
8
+ PublicKey,
9
+ Script,
10
+ Transaction,
11
+ } = require('bitcore-lib');
12
+ const addressValidator = require('wallet-address-validator');
13
+ const web3 = require('web3-utils');
14
+
15
+ const validations = {
16
+ _validate(Err, pred, x, msg) {
17
+ if (pred(x)) {
18
+ return x;
19
+ }
20
+ throw new Err(msg);
21
+ },
22
+ _validateArg(pred, x, msg) {
23
+ return this._validate(E.InvalidArguments, pred, x, msg);
24
+ },
25
+ _tryCheck(fn, arg) {
26
+ try {
27
+ fn(arg);
28
+ return true;
29
+ } catch (error) {
30
+ return false;
31
+ }
32
+ },
33
+
34
+ exists(x) {
35
+ return x != null;
36
+ },
37
+ nonEmpty(x) {
38
+ return (x != null ? x.length : undefined) > 0;
39
+ },
40
+ isNonEmptyString(x) {
41
+ return _.isString(x) && (x != null ? x.length : undefined) > 0;
42
+ },
43
+ ensureShortEnough(limit, x) {
44
+ const l = x != null ? x.length : undefined;
45
+ if (l <= limit) {
46
+ return true;
47
+ }
48
+ throw new E.BadRequest(`Max query size exceeded: ${l} / ${limit}`);
49
+ },
50
+ ensure(crit, msg) {
51
+ if (crit) {
52
+ return true;
53
+ }
54
+ throw new E.BadRequest(msg);
55
+ },
56
+ argExists(x, msg) {
57
+ return this._validateArg(this.exists, x, msg);
58
+ },
59
+
60
+ check: {
61
+ matches(required, given, msg) {
62
+ given = _.uniq(given);
63
+ const crit = _.intersection(given, required).length === given.length;
64
+ if (crit) {
65
+ return true;
66
+ }
67
+ throw new E.BadRequest(msg);
68
+ },
69
+ wasFound(type) {
70
+ return (item) => {
71
+ if (item == null) {
72
+ throw new E.NotFound(`Unknown ${type}`);
73
+ } else {
74
+ return item;
75
+ }
76
+ };
77
+ },
78
+ wasCreated(msg) {
79
+ return (arr) => {
80
+ if (!arr[1]) {
81
+ throw new E.BadRequest(msg);
82
+ } else {
83
+ return arr;
84
+ }
85
+ };
86
+ },
87
+ isTrue(crit, msg) {
88
+ if (crit) {
89
+ return true;
90
+ }
91
+ throw new E.BadRequest(msg);
92
+ },
93
+ isFalse(crit, msg) {
94
+ if (!crit) {
95
+ return true;
96
+ }
97
+ throw new E.BadRequest(msg);
98
+ },
99
+ },
100
+
101
+ isCustomerReference(x) {
102
+ return (
103
+ _.isString(x) &&
104
+ (x != null ? x.length : undefined) > 0 &&
105
+ (x != null ? x.length : undefined) <= 100
106
+ );
107
+ },
108
+ isCustomerLabelName(x) {
109
+ return (
110
+ _.isString(x) &&
111
+ (x != null ? x.length : undefined) > 0 &&
112
+ (x != null ? x.length : undefined) <= 50
113
+ );
114
+ },
115
+ ethereum: {
116
+ isAddress(str) {
117
+ return web3.isAddress(str);
118
+ },
119
+ isBlockHash(str) {
120
+ return str.length === 66 && web3.isHexStrict(str);
121
+ },
122
+ isTxHash(str) {
123
+ return str.length === 66 && web3.isHexStrict(str);
124
+ },
125
+ isAddressCode(str) {
126
+ return web3.isHexStrict(str);
127
+ },
128
+ isValidWeiAmount(str) {
129
+ try {
130
+ web3.fromWei(str);
131
+ return true;
132
+ } catch (e) {
133
+ return false;
134
+ }
135
+ },
136
+ },
137
+ bitcoin: {
138
+ isAddress(str) {
139
+ const network =
140
+ process.env.BITCOIN_NETWORK === 'testnet' ? 'testnet' : 'livenet';
141
+ return typeof str === 'string' && addressValidator.validate(str.trim(), 'BTC', network);
142
+ },
143
+ isHDPublicKey(str) {
144
+ try {
145
+ HDPublicKey(str);
146
+ return true;
147
+ } catch (error) {
148
+ return false;
149
+ }
150
+ },
151
+ isPublicKey(str) {
152
+ try {
153
+ PublicKey(str);
154
+ return true;
155
+ } catch (error) {
156
+ return false;
157
+ }
158
+ },
159
+ isTxHash(str) {
160
+ if (!V.isHexadecimal(`${str}`)) {
161
+ return false;
162
+ }
163
+ return str.length === 64;
164
+ },
165
+ isTxHex(str) {
166
+ if (!V.isHexadecimal(`${str}`)) {
167
+ return false;
168
+ }
169
+ try {
170
+ Transaction(str);
171
+ return true;
172
+ } catch (error) {
173
+ return false;
174
+ }
175
+ },
176
+ isHDPath(str) {
177
+ return HDPrivateKey.isValidPath(str);
178
+ },
179
+ isScriptHex(str) {
180
+ if (!V.isHexadecimal(`${str}`)) {
181
+ return false;
182
+ }
183
+ try {
184
+ Script(str);
185
+ return true;
186
+ } catch (error) {
187
+ return false;
188
+ }
189
+ },
190
+ isTxSignature(str) {
191
+ if (!V.isHexadecimal(`${str}`)) {
192
+ return false;
193
+ }
194
+ try {
195
+ const b = Buffer.from(str, 'hex');
196
+ Signature.fromTxFormat(b);
197
+ return true;
198
+ } catch (error) {
199
+ return false;
200
+ }
201
+ },
202
+ isBlockHeight(obj) {
203
+ return _.isInteger(+obj) && parseInt(obj, 10) >= 0;
204
+ },
205
+ },
206
+ bitcoinCash: {
207
+ isAddress(str) {
208
+ return /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}|^(bitcoincash:)?[q|p][a-z0-9]{41}|^(BITCOINCASH:)?[Q|P][A-Z0-9]{41}/.test(str);
209
+ },
210
+ isTxHash(str) {
211
+ if (!V.isHexadecimal(`${str}`)) {
212
+ return false;
213
+ }
214
+ return str.length === 64;
215
+ },
216
+ },
217
+ litecoin: {
218
+ isAddress(str) {
219
+ return /^[3LM][a-km-zA-HJ-NP-Z1-9]{24,33}|^ltc1[ac-hj-np-z02-9]{6,86}|^LTC1[AC-HJ-NP-Z02-9]{6,86}/.test(str);
220
+ },
221
+ isTxHash(str) {
222
+ if (!V.isHexadecimal(`${str}`)) {
223
+ return false;
224
+ }
225
+ return str.length === 64;
226
+ },
227
+ }
228
+ };
229
+
230
+ Object.keys(V).forEach((k) => {
231
+ const v = V[k];
232
+ // don't trigger validation with null values (prevents swagger 500 error)
233
+ validations[k] = x => (x === null ? false : v(x));
234
+ });
235
+
236
+ module.exports = validations;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ellipticltd/aml-utils",
3
- "version": "0.1.1-ts.3",
3
+ "version": "0.1.1",
4
4
  "description": "Utilities, helpers, validations, type-checking, etc",
5
5
  "engines": {
6
6
  "node": "10.1.0",
@@ -11,13 +11,10 @@
11
11
  "url": "git+ssh://git@bitbucket.org/elliptic/aml-utils.git"
12
12
  },
13
13
  "homepage": "https://bitbucket.org/elliptic/aml-utils#readme",
14
- "main": "dist/index.js",
15
- "types": "dist/index.d.ts",
14
+ "main": "index.js",
16
15
  "scripts": {
17
- "prepublish": "npm run build",
18
- "test": "npm run build && mocha --reporter spec --compilers coffee:coffee-script/register --require 'test/index.coffee'",
19
- "lint": "tslint --project tsconfig.json -c tslint.json lib/**/*.ts",
20
- "build": "tsc",
16
+ "test": "./node_modules/.bin/mocha --reporter spec --compilers coffee:coffee-script/register --require 'test/index.coffee'",
17
+ "compile": "./node_modules/coffee-script/bin/coffee -c index.coffee **/*.coffee",
21
18
  "preprocess": "npm run compile"
22
19
  },
23
20
  "author": "Adam Joyce <adam@ellipitc.co>",
@@ -33,18 +30,11 @@
33
30
  },
34
31
  "devDependencies": {
35
32
  "@ellipticltd/eslint-config": "0.0.3",
36
- "@types/express": "^4.17.0",
37
- "@types/lodash": "^4.14.135",
38
- "@types/node": "^12.0.10",
39
- "@types/validator": "^10.11.1",
40
33
  "chai": "4.1.1",
41
34
  "coffee-script": "1.12.7",
42
35
  "eslint": "5.9.0",
43
36
  "eslint-config-airbnb-base": "13.1.0",
44
37
  "eslint-plugin-import": "^2.18.0",
45
- "mocha": "3.5.0",
46
- "tslint": "^5.18.0",
47
- "tslint-config-airbnb": "^5.11.1",
48
- "typescript": "^3.5.2"
38
+ "mocha": "3.5.0"
49
39
  }
50
40
  }
@@ -1,12 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <module type="WEB_MODULE" version="4">
3
- <component name="NewModuleRootManager">
4
- <content url="file://$MODULE_DIR$">
5
- <excludeFolder url="file://$MODULE_DIR$/.tmp" />
6
- <excludeFolder url="file://$MODULE_DIR$/temp" />
7
- <excludeFolder url="file://$MODULE_DIR$/tmp" />
8
- </content>
9
- <orderEntry type="inheritedJdk" />
10
- <orderEntry type="sourceFolder" forTests="false" />
11
- </component>
12
- </module>
@@ -1,52 +0,0 @@
1
- <component name="ProjectCodeStyleConfiguration">
2
- <code_scheme name="Project" version="173">
3
- <DBN-PSQL>
4
- <case-options enabled="false">
5
- <option name="KEYWORD_CASE" value="lower" />
6
- <option name="FUNCTION_CASE" value="lower" />
7
- <option name="PARAMETER_CASE" value="lower" />
8
- <option name="DATATYPE_CASE" value="lower" />
9
- <option name="OBJECT_CASE" value="preserve" />
10
- </case-options>
11
- <formatting-settings enabled="false" />
12
- </DBN-PSQL>
13
- <DBN-SQL>
14
- <case-options enabled="false">
15
- <option name="KEYWORD_CASE" value="lower" />
16
- <option name="FUNCTION_CASE" value="lower" />
17
- <option name="PARAMETER_CASE" value="lower" />
18
- <option name="DATATYPE_CASE" value="lower" />
19
- <option name="OBJECT_CASE" value="preserve" />
20
- </case-options>
21
- <formatting-settings enabled="false">
22
- <option name="STATEMENT_SPACING" value="one_line" />
23
- <option name="CLAUSE_CHOP_DOWN" value="chop_down_if_statement_long" />
24
- <option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
25
- </formatting-settings>
26
- </DBN-SQL>
27
- <DBN-PSQL>
28
- <case-options enabled="false">
29
- <option name="KEYWORD_CASE" value="lower" />
30
- <option name="FUNCTION_CASE" value="lower" />
31
- <option name="PARAMETER_CASE" value="lower" />
32
- <option name="DATATYPE_CASE" value="lower" />
33
- <option name="OBJECT_CASE" value="preserve" />
34
- </case-options>
35
- <formatting-settings enabled="false" />
36
- </DBN-PSQL>
37
- <DBN-SQL>
38
- <case-options enabled="false">
39
- <option name="KEYWORD_CASE" value="lower" />
40
- <option name="FUNCTION_CASE" value="lower" />
41
- <option name="PARAMETER_CASE" value="lower" />
42
- <option name="DATATYPE_CASE" value="lower" />
43
- <option name="OBJECT_CASE" value="preserve" />
44
- </case-options>
45
- <formatting-settings enabled="false">
46
- <option name="STATEMENT_SPACING" value="one_line" />
47
- <option name="CLAUSE_CHOP_DOWN" value="chop_down_if_statement_long" />
48
- <option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
49
- </formatting-settings>
50
- </DBN-SQL>
51
- </code_scheme>
52
- </component>