3 const { common
} = require('@squeep/api-dingus');
5 const { randomBytes
} = require('crypto');
6 const { promisify
} = require('util');
7 const randomBytesAsync
= promisify(randomBytes
);
10 * Limit length of string to keep logs sane
15 const logTruncate
= (str
, len
) => {
16 if (typeof str
!== 'string' || str
.toString().length
<= len
) {
19 return str
.toString().slice(0, len
) + `... (${str.toString().length} bytes)`;
23 * Turn a snake into a camel.
24 * @param {String} snakeCase
25 * @param {String|RegExp} delimiter
28 const camelfy
= (snakeCase
, delimiter
= '_') => {
29 if (!snakeCase
|| typeof snakeCase
.split
!== 'function') {
32 const words
= snakeCase
.split(delimiter
);
35 ...words
.map((word
) => word
.charAt(0).toUpperCase() + word
.slice(1)),
40 * Return an array containing x if x is not an array.
43 const ensureArray
= (x
) => {
44 if (x
=== undefined) {
47 if (!Array
.isArray(x
)) {
54 * Recursively freeze an object.
58 const freezeDeep
= (o
) => {
60 Object
.getOwnPropertyNames(o
).forEach((prop
) => {
61 if (Object
.hasOwnProperty
.call(o
, prop
)
62 && ['object', 'function'].includes(typeof o
[prop
]) // eslint-disable-line security/detect-object-injection
63 && !Object
.isFrozen(o
[prop
])) { // eslint-disable-line security/detect-object-injection
64 return freezeDeep(o
[prop
]); // eslint-disable-line security/detect-object-injection
72 * %x20-21 / %x23-5B / %x5D-7E
73 * @param {String} char
75 const validErrorChar
= (char) => {
76 const value
= char.charCodeAt(0);
77 return value
=== 0x20 || value
=== 0x21
78 || (value
>= 0x23 && value
<= 0x5b)
79 || (value
>= 0x5d && value
<= 0x7e);
84 * Determine if an OAuth error message is valid.
85 * @param {String} error
88 const validError
= (error
) => {
89 return error
&& error
.split('').filter((c
) => !validErrorChar(c
)).length
=== 0 || false;
95 * scope-token = 1*( %x21 / %x23-5B / %x5D-7E )
96 * @param {String} char
98 const validScopeChar
= (char) => {
99 const value
= char.charCodeAt(0);
100 return value
=== 0x21
101 || (value
>= 0x23 && value
<= 0x5b)
102 || (value
>= 0x5d && value
<= 0x7e);
107 * Determine if a scope has a valid name.
108 * @param {String} scope
111 const validScope
= (scope
) => {
112 return scope
&& scope
.split('').filter((c
) => !validScopeChar(c
)).length
=== 0 || false;
118 * @param {Number} bytes
120 const newSecret
= async (bytes
= 64) => {
121 return (await
randomBytesAsync(bytes
* 3 / 4)).toString('base64');
126 * Convert a Date object to epoch seconds.
127 * @param {Date=} date
130 const dateToEpoch
= (date
) => {
131 const dateMs
= date
? date
.getTime() : Date
.now();
132 return Math
.ceil(dateMs
/ 1000);
136 const omit
= (o
, props
) => {
137 return Object
.fromEntries(Object
.entries(o
).filter(([k
]) => !props
.includes(k
)))
142 * Log Mystery Box statistics events.
143 * @param {Console} logger
144 * @param {String} scope
146 const mysteryBoxLogger
= (logger
, scope
) => {
148 logger
.debug(scope
, `${s.packageName}@${s.packageVersion}:${s.method}`, omit(s
, [