chore:little fixes and formating and linting and patches
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
export * from './server/get-client-ip';
|
||||
export * from './src/date';
|
||||
export * from './src/timezones';
|
||||
export * from './src/object';
|
||||
export * from './src/names';
|
||||
export * from './src/string';
|
||||
export * from './src/math';
|
||||
export * from './src/slug';
|
||||
export * from './src/url';
|
||||
export * from './src/id';
|
||||
export * from './src/get-previous-metric';
|
||||
export * from './src/group-by-labels';
|
||||
export * from './server/get-client-ip';
|
||||
export * from './src/id';
|
||||
export * from './src/math';
|
||||
export * from './src/names';
|
||||
export * from './src/object';
|
||||
export * from './src/slug';
|
||||
export * from './src/string';
|
||||
export * from './src/timezones';
|
||||
export * from './src/url';
|
||||
|
||||
@@ -16,13 +16,15 @@ export function generateSalt() {
|
||||
*/
|
||||
export async function hashPassword(
|
||||
password: string,
|
||||
keyLength = 32,
|
||||
keyLength = 32
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// generate random 16 bytes long salt - recommended by NodeJS Docs
|
||||
const salt = generateSalt();
|
||||
scrypt(password, salt, keyLength, (err, derivedKey) => {
|
||||
if (err) reject(err);
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
// derivedKey is of type Buffer
|
||||
resolve(`${salt}.${derivedKey.toString('hex')}`);
|
||||
});
|
||||
@@ -38,7 +40,7 @@ export async function hashPassword(
|
||||
export async function verifyPassword(
|
||||
password: string,
|
||||
hash: string,
|
||||
keyLength = 32,
|
||||
keyLength = 32
|
||||
): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const [salt, hashKey] = hash.split('.');
|
||||
@@ -50,10 +52,7 @@ export async function verifyPassword(
|
||||
}
|
||||
// compare the new supplied password with the hashed password using timeSafeEqual
|
||||
resolve(
|
||||
timingSafeEqual(
|
||||
new Uint8Array(hashKeyBuff),
|
||||
new Uint8Array(derivedKey),
|
||||
),
|
||||
timingSafeEqual(new Uint8Array(hashKeyBuff), new Uint8Array(derivedKey))
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -90,7 +90,7 @@ function isValidIp(ip: string): boolean {
|
||||
|
||||
export function getClientIpFromHeaders(
|
||||
headers: Record<string, string | string[] | undefined> | Headers,
|
||||
overrideHeaderName?: string,
|
||||
overrideHeaderName?: string
|
||||
): {
|
||||
ip: string;
|
||||
header: string;
|
||||
@@ -116,7 +116,9 @@ export function getClientIpFromHeaders(
|
||||
}
|
||||
}
|
||||
|
||||
if (!value) continue;
|
||||
if (!value) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle x-forwarded-for (comma separated)
|
||||
if (headerName === 'x-forwarded-for') {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export * from './crypto';
|
||||
export * from './profileId';
|
||||
export * from './parser-user-agent';
|
||||
export * from './parse-referrer';
|
||||
export * from './id';
|
||||
export * from './parse-referrer';
|
||||
export * from './parser-user-agent';
|
||||
export * from './profileId';
|
||||
|
||||
@@ -107,7 +107,7 @@ describe('getReferrerWithQuery', () => {
|
||||
utm_source: 'google',
|
||||
ref: 'facebook',
|
||||
utm_referrer: 'twitter',
|
||||
}),
|
||||
})
|
||||
).toEqual({
|
||||
name: 'Google',
|
||||
type: 'search',
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { stripTrailingSlash } from '../src/string';
|
||||
|
||||
import referrers from './referrers';
|
||||
|
||||
function getHostname(url: string | undefined) {
|
||||
@@ -26,7 +25,7 @@ export function parseReferrer(url: string | undefined) {
|
||||
}
|
||||
|
||||
export function getReferrerWithQuery(
|
||||
query: Record<string, string> | undefined,
|
||||
query: Record<string, string> | undefined
|
||||
) {
|
||||
if (!query) {
|
||||
return null;
|
||||
@@ -47,7 +46,7 @@ export function getReferrerWithQuery(
|
||||
referrers[source] ||
|
||||
referrers[`${source}.com`] ||
|
||||
Object.values(referrers).find(
|
||||
(referrer) => referrer.name.toLowerCase() === source,
|
||||
(referrer) => referrer.name.toLowerCase() === source
|
||||
);
|
||||
|
||||
if (match) {
|
||||
|
||||
@@ -201,7 +201,7 @@ describe('getDevice', () => {
|
||||
describe('parseUserAgent - brand detection', () => {
|
||||
it('should detect Xiaomi brand from Manufacturer field', () => {
|
||||
const result = parseUserAgent(
|
||||
'App/1.0 (Android 12; Model=POCO X5; Manufacturer=Xiaomi)',
|
||||
'App/1.0 (Android 12; Model=POCO X5; Manufacturer=Xiaomi)'
|
||||
);
|
||||
expect(result.brand).toBe('Xiaomi');
|
||||
expect(result.model).toBe('POCO X5');
|
||||
@@ -210,7 +210,7 @@ describe('parseUserAgent - brand detection', () => {
|
||||
|
||||
it('should detect Samsung brand from model name', () => {
|
||||
const result = parseUserAgent(
|
||||
'App/1.0 (Android 13; Model=Galaxy S23 Ultra)',
|
||||
'App/1.0 (Android 13; Model=Galaxy S23 Ultra)'
|
||||
);
|
||||
expect(result.brand).toBe('Samsung');
|
||||
expect(result.model).toBe('Galaxy S23 Ultra');
|
||||
@@ -228,7 +228,7 @@ describe('parseUserAgent - brand detection', () => {
|
||||
|
||||
it('should detect OnePlus', () => {
|
||||
const result = parseUserAgent(
|
||||
'App/1.0 (Android 13; Model=OnePlus 11; Manufacturer=OnePlus)',
|
||||
'App/1.0 (Android 13; Model=OnePlus 11; Manufacturer=OnePlus)'
|
||||
);
|
||||
expect(result.brand).toBe('OnePlus');
|
||||
expect(result.model).toBe('OnePlus 11');
|
||||
@@ -239,7 +239,7 @@ describe('parseUserAgent - brand detection', () => {
|
||||
|
||||
it('should detect Huawei', () => {
|
||||
const result = parseUserAgent(
|
||||
'App/1.0 (Android 12; Model=P60 Pro; Manufacturer=Huawei)',
|
||||
'App/1.0 (Android 12; Model=P60 Pro; Manufacturer=Huawei)'
|
||||
);
|
||||
expect(result.brand).toBe('Huawei');
|
||||
expect(result.model).toBe('P60 Pro');
|
||||
|
||||
@@ -14,9 +14,9 @@ const parsedServerUa = {
|
||||
|
||||
// Pre-compile all regex patterns for better performance
|
||||
const IPHONE_MODEL_REGEX = /(iPhone|iPad)\s*([0-9,]+)/i;
|
||||
const IOS_MODEL_REGEX = /(iOS)\s*([0-9\.]+)/i;
|
||||
const IOS_MODEL_REGEX = /(iOS)\s*([0-9.]+)/i;
|
||||
const IPAD_OS_VERSION_REGEX = /iPadOS\s*([0-9_]+)/i;
|
||||
const SINGLE_NAME_VERSION_REGEX = /^[^\/]+\/[\d.]+$/;
|
||||
const SINGLE_NAME_VERSION_REGEX = /^[^/]+\/[\d.]+$/;
|
||||
|
||||
// App-style UA patterns (e.g., "Model=Redmi Note 8 Pro; Manufacturer=Xiaomi")
|
||||
const APP_MODEL_REGEX = /Model=([^;)]+)/i;
|
||||
@@ -150,7 +150,9 @@ function detectBrand(ua: string, model?: string): string | undefined {
|
||||
|
||||
// Check if a model name indicates a phone (not tablet)
|
||||
function isKnownPhoneModel(model?: string): boolean {
|
||||
if (!model) return false;
|
||||
if (!model) {
|
||||
return false;
|
||||
}
|
||||
return KNOWN_PHONE_PATTERNS.some((pattern) => pattern.test(model));
|
||||
}
|
||||
|
||||
@@ -166,7 +168,7 @@ const parse = (ua: string): UAParser.IResult => {
|
||||
|
||||
// Some user agents are not detected correctly by ua-parser-js
|
||||
// Doing some extra checks for ios
|
||||
if (!res.device.model && !res.os.name) {
|
||||
if (!(res.device.model || res.os.name)) {
|
||||
const iphone = isIphone(ua);
|
||||
if (iphone) {
|
||||
const result = {
|
||||
@@ -213,8 +215,8 @@ const parse = (ua: string): UAParser.IResult => {
|
||||
...res,
|
||||
device: {
|
||||
...res.device,
|
||||
model: model,
|
||||
vendor: vendor,
|
||||
model,
|
||||
vendor,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -242,9 +244,11 @@ export type UserAgentInfo = ReturnType<typeof parseUserAgent>;
|
||||
export type UserAgentResult = ReturnType<typeof parseUserAgent>;
|
||||
export function parseUserAgent(
|
||||
ua?: string | null,
|
||||
overrides?: Record<string, unknown>,
|
||||
overrides?: Record<string, unknown>
|
||||
) {
|
||||
if (!ua) return parsedServerUa;
|
||||
if (!ua) {
|
||||
return parsedServerUa;
|
||||
}
|
||||
const res = parse(ua);
|
||||
|
||||
if (isServer(res)) {
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { isNil } from 'ramda';
|
||||
|
||||
import type { PreviousValue } from '@openpanel/validation';
|
||||
|
||||
import { isNil } from 'ramda';
|
||||
import { round } from './math';
|
||||
|
||||
export function getPreviousMetric(
|
||||
current: number,
|
||||
previous: number | null | undefined,
|
||||
previous: number | null | undefined
|
||||
): PreviousValue {
|
||||
if (isNil(previous)) {
|
||||
return undefined;
|
||||
@@ -20,7 +18,7 @@ export function getPreviousMetric(
|
||||
: 0) -
|
||||
1) *
|
||||
100,
|
||||
1,
|
||||
1
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -54,7 +54,7 @@ export function groupByLabels(data: ISerieDataItem[]): GroupedResult[] {
|
||||
const result = Array.from(groupedMap.values()).map((group) => ({
|
||||
...group,
|
||||
data: group.data.sort(
|
||||
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(),
|
||||
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()
|
||||
),
|
||||
}));
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ export const average = (arr: (number | null)[], includeZero = false) => {
|
||||
isNumber(n) &&
|
||||
!Number.isNaN(n) &&
|
||||
Number.isFinite(n) &&
|
||||
(includeZero || n !== 0),
|
||||
(includeZero || n !== 0)
|
||||
);
|
||||
const avg = filtered.reduce((p, c) => p + c, 0) / filtered.length;
|
||||
return Number.isNaN(avg) ? 0 : avg;
|
||||
@@ -22,13 +22,17 @@ export const sum = (arr: (number | null | undefined)[]): number =>
|
||||
|
||||
export const min = (arr: (number | null | undefined)[]): number => {
|
||||
const filtered = arr.filter(isNumber);
|
||||
if (filtered.length === 0) return 0;
|
||||
if (filtered.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
return filtered.reduce((a, b) => (b < a ? b : a), filtered[0]!);
|
||||
};
|
||||
|
||||
export const max = (arr: (number | null | undefined)[]): number => {
|
||||
const filtered = arr.filter(isNumber);
|
||||
if (filtered.length === 0) return 0;
|
||||
if (filtered.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
return filtered.reduce((a, b) => (b > a ? b : a), filtered[0]!);
|
||||
};
|
||||
|
||||
@@ -36,5 +40,5 @@ export const isFloat = (n: number) => n % 1 !== 0;
|
||||
|
||||
export const ifNaN = <T extends number>(
|
||||
n: number | null | undefined,
|
||||
defaultValue: T,
|
||||
defaultValue: T
|
||||
): T => (Number.isNaN(n) ? defaultValue : (n as T));
|
||||
|
||||
@@ -10,7 +10,7 @@ describe('toDots', () => {
|
||||
arrayWithObjects: [{ a: 1 }, { b: 2 }, { c: 3 }],
|
||||
objectWithArrays: { a: [1, 2, 3] },
|
||||
null: null,
|
||||
undefined: undefined,
|
||||
undefined,
|
||||
empty: '',
|
||||
jsonString: '{"a": 1, "b": 2}',
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ function isMalformedJsonString(value: string): boolean {
|
||||
|
||||
export function toDots(
|
||||
obj: Record<string, unknown>,
|
||||
path = '',
|
||||
path = ''
|
||||
): Record<string, string> {
|
||||
// Clickhouse breaks on insert if a property contains invalid surrogate pairs
|
||||
function removeInvalidSurrogates(value: string): string {
|
||||
@@ -67,7 +67,7 @@ export function toDots(
|
||||
}
|
||||
|
||||
export function toObject(
|
||||
obj: Record<string, string | undefined>,
|
||||
obj: Record<string, string | undefined>
|
||||
): Record<string, unknown> {
|
||||
let result: Record<string, unknown> = {};
|
||||
Object.entries(obj).forEach(([key, value]) => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { slug } from './slug';
|
||||
describe('slug', () => {
|
||||
it('should remove pipes from string', () => {
|
||||
expect(slug('Hello || World, | Test å å ä ä')).toBe(
|
||||
'hello-world-test-a-a-a-a',
|
||||
'hello-world-test-a-a-a-a'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ const slugify = (str: string) => {
|
||||
.replaceAll('Ä', 'A')
|
||||
.replaceAll('Ö', 'O')
|
||||
.replace(/\|+/g, '-'),
|
||||
{ lower: true, strict: true, trim: true },
|
||||
{ lower: true, strict: true, trim: true }
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export function parseSearchParams(
|
||||
params: URLSearchParams,
|
||||
params: URLSearchParams
|
||||
): Record<string, string> | undefined {
|
||||
const result: Record<string, string> = {};
|
||||
for (const [key, value] of params.entries()) {
|
||||
@@ -25,7 +25,7 @@ export function parsePath(path?: string): {
|
||||
|
||||
// If path does not have a leading /,
|
||||
// its probably a named route
|
||||
if (!path.startsWith('/') && !hasOrigin) {
|
||||
if (!(path.startsWith('/') || hasOrigin)) {
|
||||
return {
|
||||
path,
|
||||
origin: '',
|
||||
@@ -50,9 +50,9 @@ export function parsePath(path?: string): {
|
||||
|
||||
export function isSameDomain(
|
||||
url1: string | undefined,
|
||||
url2: string | undefined,
|
||||
url2: string | undefined
|
||||
) {
|
||||
if (!url1 || !url2) {
|
||||
if (!(url1 && url2)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { getSharedVitestConfig } from '../../vitest.shared';
|
||||
|
||||
export default getSharedVitestConfig({ __dirname });
|
||||
export default getSharedVitestConfig({ __dirname: import.meta.dirname });
|
||||
|
||||
Reference in New Issue
Block a user