chore:little fixes and formating and linting and patches

This commit is contained in:
2026-03-31 15:50:54 +02:00
parent a1ce71ffb6
commit 9b197abcfa
815 changed files with 22960 additions and 8982 deletions

View File

@@ -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';

View File

@@ -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))
);
});
});

View File

@@ -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') {

View File

@@ -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';

View File

@@ -107,7 +107,7 @@ describe('getReferrerWithQuery', () => {
utm_source: 'google',
ref: 'facebook',
utm_referrer: 'twitter',
}),
})
).toEqual({
name: 'Google',
type: 'search',

View File

@@ -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) {

View File

@@ -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');

View File

@@ -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)) {

View File

@@ -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 {

View File

@@ -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()
),
}));

View File

@@ -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));

View File

@@ -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}',
};

View File

@@ -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]) => {

View File

@@ -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'
);
});
});

View File

@@ -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 }
);
};

View File

@@ -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 {

View File

@@ -1,3 +1,3 @@
import { getSharedVitestConfig } from '../../vitest.shared';
export default getSharedVitestConfig({ __dirname });
export default getSharedVitestConfig({ __dirname: import.meta.dirname });