This commit is contained in:
2026-02-16 19:34:35 +00:00
commit 9ab0940ca4
784 changed files with 41710 additions and 0 deletions
+85
View File
@@ -0,0 +1,85 @@
import { wait } from 'helpers/wait';
type QueueItem = {
key: string;
fn: () => Promise<unknown> | unknown;
onSuccess?: () => Promise<unknown> | unknown;
onError?: (error: unknown) => Promise<unknown> | unknown;
onResolved?: () => Promise<unknown> | unknown;
};
class Queue {
private items: QueueItem[] = [];
keys = new Set<string>();
enqueue(item: QueueItem): void {
if (this.keys.has(item.key)) {
throw new Error('DUPLICATE_KEY');
}
this.keys.add(item.key);
this.items.push(item);
}
dequeue(): QueueItem | undefined {
return this.items.shift();
}
isEmpty(): boolean {
return this.items.length === 0;
}
size(): number {
return this.items.length;
}
}
export class QueueManager {
private queue = new Queue();
private isRunning = false;
add(item: QueueItem) {
try {
this.queue.enqueue(item);
if (!this.isRunning) {
this.isRunning = true;
this.run();
}
return this.queue.size();
} catch {
return {
error: 'There is already a refresh job running or in queue for this google account',
};
}
}
private async run() {
this.isRunning = true;
if (this.queue.isEmpty()) {
this.isRunning = false;
return;
}
const item = this.queue.dequeue();
if (!item) {
this.isRunning = false;
return;
}
const { key, fn, onSuccess, onError, onResolved } = item;
try {
await fn();
if (onSuccess) await onSuccess();
} catch (ex) {
if (onError) await onError(ex);
} finally {
if (onResolved) await onResolved();
this.isRunning = false;
this.queue.keys.delete(key);
if (!this.queue.isEmpty()) {
await wait(1000);
await this.run();
}
}
}
}
+6
View File
@@ -0,0 +1,6 @@
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: (boolean | string | undefined | null)[]) {
return twMerge(clsx(inputs));
}
+6
View File
@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
+10
View File
@@ -0,0 +1,10 @@
export function debounce<T extends (...args: unknown[]) => void>(cb: T, delay = 1000) {
let timeout: ReturnType<typeof setTimeout>;
return (...args: Parameters<T>) => {
clearTimeout(timeout);
timeout = setTimeout(() => {
cb(...args);
}, delay);
};
}
+331
View File
@@ -0,0 +1,331 @@
import { describe, expect, test, beforeAll, afterAll, spyOn } from 'bun:test';
import {
leftpad,
rightpad,
formatPercent,
formatTime,
formatDate,
formatDateTime,
formatCurrency,
formatNumber,
formatPercentage,
formatDiff,
daysAgo,
formatByThousands,
} from './formatters';
// Mock browser globals
const originalNavigator = globalThis.navigator;
beforeAll(() => {
// @ts-expect-error - mocking navigator for tests
globalThis.navigator = {
language: 'en-US',
languages: ['en-US'],
};
});
afterAll(() => {
globalThis.navigator = originalNavigator;
});
describe('leftpad', () => {
test('pads number with leading zeros', () => {
expect(leftpad(5, 3)).toBe('005');
expect(leftpad(42, 4)).toBe('0042');
expect(leftpad(123, 3)).toBe('123');
});
test('pads string with leading zeros', () => {
expect(leftpad('5', 3)).toBe('005');
expect(leftpad('ab', 4)).toBe('00ab');
});
test('handles value already at or exceeding size', () => {
expect(leftpad(12345, 3)).toBe('12345');
expect(leftpad('abc', 3)).toBe('abc');
});
test('handles default value of 0', () => {
expect(leftpad(undefined, 3)).toBe('000');
});
});
describe('rightpad', () => {
test('pads number with trailing zeros', () => {
expect(rightpad(5, 3)).toBe('500');
expect(rightpad(42, 4)).toBe('4200');
});
test('pads string with trailing zeros', () => {
expect(rightpad('5', 3)).toBe('500');
expect(rightpad('ab', 4)).toBe('ab00');
});
test('handles value already at or exceeding size', () => {
expect(rightpad(12345, 3)).toBe('12345');
});
test('handles default value of 0', () => {
expect(rightpad(undefined, 3)).toBe('000');
});
});
describe('formatPercent', () => {
test('formats number with 2 decimal places', () => {
expect(formatPercent(5.5)).toBe('05.50');
expect(formatPercent(12.34)).toBe('12.34');
expect(formatPercent(99.9)).toBe('99.90');
});
test('formats string numbers', () => {
expect(formatPercent('5.5')).toBe('05.50');
expect(formatPercent('12.34')).toBe('12.34');
});
test('handles whole numbers', () => {
expect(formatPercent(5)).toBe('05.00');
expect(formatPercent('12')).toBe('12.00');
});
test('handles zero', () => {
expect(formatPercent(0)).toBe('00.00');
expect(formatPercent('0')).toBe('00.00');
});
test('returns empty string for null/undefined', () => {
expect(formatPercent(null as unknown as number)).toBe('');
expect(formatPercent(undefined as unknown as number)).toBe('');
});
});
describe('formatPercentage', () => {
test('formats number as percentage string', () => {
expect(formatPercentage(5.5)).toBe('5.50%');
expect(formatPercentage(12.345)).toBe('12.35%');
expect(formatPercentage(0.1)).toBe('0.10%');
});
test('formats string numbers', () => {
expect(formatPercentage('5.5')).toBe('5.50%');
expect(formatPercentage('99')).toBe('99.00%');
});
test('returns default for falsy values', () => {
expect(formatPercentage(0)).toBe('00.00%');
expect(formatPercentage('')).toBe('00.00%');
});
});
describe('formatDiff', () => {
test('formats positive difference with plus sign', () => {
expect(formatDiff(5)).toBe('+5');
expect(formatDiff(10.7)).toBe('+11');
});
test('formats negative difference without extra sign', () => {
expect(formatDiff(-5)).toBe('-5');
expect(formatDiff(-10.7)).toBe('-11');
});
test('adds percent suffix when isPercent is true', () => {
expect(formatDiff(5, true)).toBe('+5%');
expect(formatDiff(-3, true)).toBe('-3%');
});
test('shows decimals when decimals is true', () => {
expect(formatDiff(5.55, false, true)).toBe('+5.55');
expect(formatDiff(-3.14, true, true)).toBe('-3.14%');
});
test('returns empty string for falsy values', () => {
expect(formatDiff(0)).toBe('');
expect(formatDiff('')).toBe('');
});
});
describe('daysAgo', () => {
const oneDay = 1000 * 60 * 60 * 24;
test('calculates days between two timestamps', () => {
const now = Date.now();
expect(daysAgo(now, now - oneDay)).toBe(1);
expect(daysAgo(now, now - oneDay * 5)).toBe(5);
});
test('order of arguments does not matter (absolute difference)', () => {
const now = Date.now();
const past = now - oneDay * 3;
expect(daysAgo(now, past)).toBe(3);
expect(daysAgo(past, now)).toBe(3);
});
test('same timestamp returns 0', () => {
const now = Date.now();
expect(daysAgo(now, now)).toBe(0);
});
test('partial days are rounded up', () => {
const now = Date.now();
const halfDayAgo = now - oneDay * 0.5;
expect(daysAgo(now, halfDayAgo)).toBe(1);
});
});
describe('formatByThousands', () => {
test('formats millions with M suffix', () => {
const result = formatByThousands(1500000);
expect(result).toContain('M');
expect(result).toContain('1');
});
test('formats thousands with K suffix', () => {
const result = formatByThousands(5000);
expect(result).toContain('K');
expect(result).toContain('5');
});
test('formats small numbers without suffix', () => {
const result = formatByThousands(500);
expect(result).not.toContain('K');
expect(result).not.toContain('M');
});
test('handles zero', () => {
const result = formatByThousands(0);
expect(result).toContain('0');
});
test('handles exactly 1000', () => {
const result = formatByThousands(1000);
expect(result).toContain('K');
});
test('handles exactly 1000000', () => {
const result = formatByThousands(1000000);
expect(result).toContain('M');
});
});
describe('formatTime', () => {
test('formats timestamp to time string', () => {
const mockToLocaleTimeString = spyOn(Date.prototype, 'toLocaleTimeString').mockReturnValue('10:30:00 AM');
const result = formatTime(1706180400000);
expect(result).toBe('10:30:00 AM');
mockToLocaleTimeString.mockRestore();
});
test('returns empty string for falsy values', () => {
expect(formatTime(0)).toBe('');
expect(formatTime('')).toBe('');
});
});
describe('formatDate', () => {
test('formats timestamp to date string', () => {
const mockToLocaleDateString = spyOn(Date.prototype, 'toLocaleDateString').mockReturnValue('1/25/2024');
const result = formatDate(1706180400000);
expect(result).toBe('1/25/2024');
mockToLocaleDateString.mockRestore();
});
test('returns empty string for falsy values', () => {
expect(formatDate(0)).toBe('');
expect(formatDate('')).toBe('');
});
});
describe('formatDateTime', () => {
test('formats timestamp to date and time string', () => {
const mockToLocaleDateString = spyOn(Date.prototype, 'toLocaleDateString').mockReturnValue('1/25/2024');
const mockToLocaleTimeString = spyOn(Date.prototype, 'toLocaleTimeString').mockReturnValue('10:30:00 AM');
const result = formatDateTime(1706180400000);
expect(result).toBe('1/25/2024 10:30:00 AM');
mockToLocaleDateString.mockRestore();
mockToLocaleTimeString.mockRestore();
});
test('returns empty string for falsy values', () => {
expect(formatDateTime(0)).toBe('');
expect(formatDateTime('')).toBe('');
});
});
describe('formatCurrency', () => {
const OriginalNumberFormat = Intl.NumberFormat;
afterAll(() => {
globalThis.Intl.NumberFormat = OriginalNumberFormat;
});
test('formats number as currency', () => {
// @ts-expect-error - mocking Intl.NumberFormat
globalThis.Intl.NumberFormat = class {
format = () => '$1,234.56';
};
const result = formatCurrency(1234.56, 'USD');
expect(result).toBe('$1,234.56');
});
test('handles string input', () => {
// @ts-expect-error - mocking Intl.NumberFormat
globalThis.Intl.NumberFormat = class {
format = () => '$99.00';
};
const result = formatCurrency('99', 'USD');
expect(result).toBe('$99.00');
});
test('handles zero and NaN', () => {
// @ts-expect-error - mocking Intl.NumberFormat
globalThis.Intl.NumberFormat = class {
format = () => '$0.00';
};
expect(formatCurrency(0, 'USD')).toBe('$0.00');
expect(formatCurrency(NaN, 'USD')).toBe('$0.00');
});
test('respects decimals parameter', () => {
// @ts-expect-error - mocking Intl.NumberFormat
globalThis.Intl.NumberFormat = class {
format = () => '€10.000';
};
const result = formatCurrency(10, 'EUR', 3);
expect(result).toBe('€10.000');
});
});
describe('formatNumber', () => {
const OriginalNumberFormat = Intl.NumberFormat;
afterAll(() => {
globalThis.Intl.NumberFormat = OriginalNumberFormat;
});
test('formats number with locale formatting', () => {
// @ts-expect-error - mocking Intl.NumberFormat
globalThis.Intl.NumberFormat = class {
format = () => '1,234,567';
};
const result = formatNumber(1234567);
expect(result).toBe('1,234,567');
});
test('handles string input', () => {
// @ts-expect-error - mocking Intl.NumberFormat
globalThis.Intl.NumberFormat = class {
format = () => '999';
};
const result = formatNumber('999');
expect(result).toBe('999');
});
test('handles zero and NaN', () => {
// @ts-expect-error - mocking Intl.NumberFormat
globalThis.Intl.NumberFormat = class {
format = () => '0';
};
expect(formatNumber(0)).toBe('0');
expect(formatNumber(NaN)).toBe('0');
});
});
+69
View File
@@ -0,0 +1,69 @@
export function leftpad(value: number | string = 0, size: number) {
let s = value + '';
while (s.length < size) s = '0' + s;
return s;
}
export function rightpad(value: number | string = 0, size: number) {
let s = value + '';
while (s.length < size) s = s + '0';
return s;
}
export function formatPercent(value: number | string) {
if (typeof value === 'undefined' || value === null) return '';
const [left, right] = value.toString().split('.');
return `${leftpad(left || 0, 2)}.${rightpad(right || 0, 2)}`;
}
export const formatTime = (value: number | string) => {
return !!value ? new Date(value).toLocaleTimeString() : '';
};
export const formatDate = (value: number | string) => {
return !!value ? new Date(value).toLocaleDateString() : '';
};
export const formatDateTime = (value: number | string) => {
return !!value ? `${formatDate(value)} ${formatTime(value)}` : '';
};
export function formatCurrency(value: number | string, currency: string, decimals: number = 2) {
const locale = navigator.language || navigator.languages[0];
const formatter = new Intl.NumberFormat(locale, {
style: 'currency',
currency,
});
let valueToFormat = (+value).toFixed(decimals);
return formatter.format(+valueToFormat || 0);
}
export function formatNumber(value: number | string) {
const locale = navigator.language || navigator.languages[0];
const formatter = new Intl.NumberFormat(locale);
return formatter.format(+value || 0);
}
export const formatPercentage = (value: string | number) => (value ? `${(+value).toFixed(2)}%` : '00.00%');
export const formatDiff = (value: string | number, isPercent = false, decimals = false) =>
value ? `${+value > 0 ? '+' : ''}${(+value).toFixed(decimals ? 2 : 0)}${isPercent ? '%' : ''}` : '';
export function daysAgo(first: number, last: number) {
return Math.ceil(Math.abs(first - last) / (1000 * 60 * 60 * 24));
}
export function formatByThousands(value: number) {
const locale = navigator.language || navigator.languages[0];
const formatter = new Intl.NumberFormat(locale, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
if (value >= 1000000) {
return `${formatter.format(value / 1000000)}M`;
}
if (value >= 1000) {
return `${formatter.format(value / 1000)}K`;
}
return formatter.format(value);
}
@@ -0,0 +1,18 @@
let isListening = false;
export function listenForUrlChange(cb: (url: string) => void) {
let currentUrl = window.location.href;
function listen() {
if (window.location.href !== currentUrl) {
currentUrl = window.location.href;
cb(currentUrl);
}
requestAnimationFrame(listen);
}
if (!isListening) {
isListening = true;
listen();
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"name": "helpers",
"version": "1.0.0",
"license": "MIT"
}
+61
View File
@@ -0,0 +1,61 @@
// Frontend
type PasskeyUser = {
id: string;
email: string;
firstName: string;
lastName: string;
};
export function arrayBufferToBase64(buffer: ArrayBuffer) {
const bytes = new Uint8Array(buffer);
const binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join('');
return window.btoa(binary);
}
export function getCredentialPayload(challenge: number[], user: PasskeyUser) {
return {
challenge: new Uint8Array(challenge),
rp: {
name: 'Pertento AI',
},
user: {
id: Uint8Array.from(atob(btoa(user.id)), (c) => c.charCodeAt(0)),
name: user.email,
displayName: `${user.firstName} ${user.lastName}`,
},
pubKeyCredParams: [
{ type: 'public-key' as const, alg: -7 },
{ type: 'public-key' as const, alg: -257 },
],
};
}
export function getSigninPayload(challenge: number[], credentialIds: string[]) {
return {
challenge: new Uint8Array(challenge),
allowCredentials: credentialIds.map((id) => {
const urlSafeId = id.replace(/-/g, '+').replace(/_/g, '/');
const decodedId = atob(urlSafeId);
return {
type: 'public-key' as const,
id: Uint8Array.from(decodedId, (c) => c.charCodeAt(0)),
};
}),
userVerification: 'preferred' as const,
};
}
// Backend
export function createChallenge() {
const challenge = crypto.randomUUID();
const hex = challenge.replace(/-/g, '');
const array: number[] = new Array(hex.length / 2);
for (let i = 0; i < hex.length; i += 2) {
array[i / 2] = parseInt(hex.substr(i, 2), 16);
}
return array;
}
export const hexToString = (array: number[]) => array.map((byte) => byte.toString(16).padStart(2, '0')).join('');
@@ -0,0 +1,22 @@
export function stringifyFunction(fn: (() => void) | null | undefined, args?: Record<string, unknown>) {
if (!fn) return '';
const functionAsString = fn.toString();
const bodyStart = functionAsString.indexOf('{') + 1;
const bodyEnd = functionAsString.lastIndexOf('}');
let functionBody = functionAsString.substring(bodyStart, bodyEnd).trim();
if (args) {
for (const [key, value] of Object.entries(args)) {
const regex = new RegExp(`\\b${key}\\b`, 'g');
functionBody = functionBody.replace(regex, JSON.stringify(value));
}
}
functionBody = functionBody
.replace(/\bev\./g, 'event.')
.replace(/\}\s\= ev/g, '} = event')
.replace(/(\r\n|\n|\r)/gm, ' ')
.replace(/\s+/g, ' ');
return `(function(){${functionBody}})();`;
}
+13
View File
@@ -0,0 +1,13 @@
export function throttle<T extends (...args: unknown[]) => void>(func: T, interval: number) {
let isRunning = false;
return function (this: unknown, ...args: Parameters<T>) {
if (!isRunning) {
isRunning = true;
func.apply(this, args);
setTimeout(() => {
isRunning = false;
}, interval);
}
};
}
+153
View File
@@ -0,0 +1,153 @@
import { describe, expect, test } from 'bun:test';
import { checkUrlTargeting, testRegex } from './url-match';
describe('testRegex', () => {
test('returns true for matching regex', () => {
expect(testRegex('hello', 'hello world')).toBe(true);
expect(testRegex('^https://', 'https://example.com')).toBe(true);
expect(testRegex('/products/\\d+', '/products/123')).toBe(true);
});
test('returns false for non-matching regex', () => {
expect(testRegex('^https://', 'http://example.com')).toBe(false);
expect(testRegex('/products/\\d+$', '/products/abc')).toBe(false);
});
test('returns false for invalid regex', () => {
expect(testRegex('[invalid', 'test')).toBe(false);
expect(testRegex('(unclosed', 'test')).toBe(false);
});
});
describe('checkUrlTargeting', () => {
describe('empty targeting', () => {
test('returns true for empty targeting array', () => {
expect(checkUrlTargeting([], 'https://example.com')).toBe(true);
});
});
describe('equals condition', () => {
test('matches exact URL', () => {
const targeting = [{ url: 'https://example.com/page', condition: 'equals' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/page')).toBe(true);
});
test('does not match different URL', () => {
const targeting = [{ url: 'https://example.com/page', condition: 'equals' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/other')).toBe(false);
});
test('ignores trailing slashes', () => {
const targeting = [{ url: 'https://example.com/page/', condition: 'equals' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/page')).toBe(true);
});
});
describe('not equals condition', () => {
test('matches when URL is different', () => {
const targeting = [{ url: 'https://example.com/page', condition: 'not equals' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/other')).toBe(true);
});
test('does not match when URL is same', () => {
const targeting = [{ url: 'https://example.com/page', condition: 'not equals' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/page')).toBe(false);
});
});
describe('contains condition', () => {
test('matches when URL contains substring', () => {
const targeting = [{ url: 'example', condition: 'contains' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/page')).toBe(true);
});
test('matches partial path', () => {
const targeting = [{ url: '/products/', condition: 'contains' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/products/123')).toBe(true);
});
test('does not match when substring not present', () => {
const targeting = [{ url: 'foobar', condition: 'contains' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/page')).toBe(false);
});
});
describe('does not contain condition', () => {
test('matches when URL does not contain substring', () => {
const targeting = [{ url: 'admin', condition: 'does not contain' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/page')).toBe(true);
});
test('does not match when URL contains substring', () => {
const targeting = [{ url: 'example', condition: 'does not contain' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/page')).toBe(false);
});
});
describe('starts with condition', () => {
test('matches URL starting with prefix', () => {
const targeting = [{ url: 'https://example.com', condition: 'starts with' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/page')).toBe(true);
});
test('does not match URL with different start', () => {
const targeting = [{ url: 'https://other.com', condition: 'starts with' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/page')).toBe(false);
});
});
describe('ends with condition', () => {
test('matches URL ending with suffix', () => {
const targeting = [{ url: '/checkout', condition: 'ends with' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/checkout')).toBe(true);
});
test('does not match URL with different ending', () => {
const targeting = [{ url: '/checkout', condition: 'ends with' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/cart')).toBe(false);
});
});
describe('matches regex condition', () => {
test('matches valid regex pattern', () => {
const targeting = [{ url: '/products/\\d+', condition: 'matches regex' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/products/123')).toBe(true);
});
test('does not match non-matching pattern', () => {
const targeting = [{ url: '/products/\\d+$', condition: 'matches regex' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/products/abc')).toBe(false);
});
test('handles invalid regex gracefully', () => {
const targeting = [{ url: '[invalid', condition: 'matches regex' as const }];
expect(checkUrlTargeting(targeting, 'https://example.com/page')).toBe(false);
});
});
describe('multiple targeting rules', () => {
test('returns true if any rule matches (OR logic)', () => {
const targeting = [
{ url: '/admin', condition: 'contains' as const },
{ url: '/dashboard', condition: 'contains' as const },
];
expect(checkUrlTargeting(targeting, 'https://example.com/dashboard')).toBe(true);
});
test('returns false if no rules match', () => {
const targeting = [
{ url: '/admin', condition: 'contains' as const },
{ url: '/dashboard', condition: 'contains' as const },
];
expect(checkUrlTargeting(targeting, 'https://example.com/home')).toBe(false);
});
test('first matching rule short-circuits', () => {
const targeting = [
{ url: 'example', condition: 'contains' as const },
{ url: '[invalid', condition: 'matches regex' as const }, // invalid, but won't be checked
];
expect(checkUrlTargeting(targeting, 'https://example.com')).toBe(true);
});
});
});
+45
View File
@@ -0,0 +1,45 @@
import type { RuntimeUrlTargeting } from 'types/runtime';
export const checkUrlTargeting = (urlTargeting: RuntimeUrlTargeting[], url?: string): boolean => {
const pageUrl = (url || window.location.href).replace(/\/$/, '');
if (urlTargeting.length === 0) return true;
for (const { url: targetUrlRaw, condition } of urlTargeting) {
const targetUrl = targetUrlRaw.replace(/\/$/, '');
switch (condition) {
case 'does not contain':
if (!pageUrl.includes(targetUrl)) return true;
break;
case 'matches regex':
if (testRegex(targetUrl, pageUrl)) return true;
break;
case 'starts with':
if (pageUrl.startsWith(targetUrl)) return true; // ✅ fixed
break;
case 'contains':
if (pageUrl.includes(targetUrl)) return true;
break;
case 'ends with':
if (pageUrl.endsWith(targetUrl)) return true;
break;
case 'not equals':
if (pageUrl !== targetUrl) return true;
break;
case 'equals':
if (pageUrl === targetUrl) return true;
break;
}
}
return false; // none of them matched
};
export const testRegex = (regexStr: string, url: string): boolean => {
try {
return new RegExp(regexStr).test(url);
} catch {
return false;
}
};
+1
View File
@@ -0,0 +1 @@
export const wait = (ms = 1000) => new Promise((resolve) => setTimeout(resolve, ms));