/** * Small shared helpers for @pixu1980/pi-web. */ export function generateId(): string { return Date.now().toString(36) + Math.random().toString(36).slice(2, 8); } /** * Minimal promise-based semaphore that caps concurrent URL fetches. */ export class Semaphore { private active = 0; private readonly queue: Array<() => void> = []; constructor(private readonly max: number) {} async acquire(): Promise { if (this.active < this.max) { this.active++; return; } await new Promise((resolve) => { this.queue.push(resolve); }); this.active++; } release(): void { this.active--; const next = this.queue.shift(); if (next) { next(); } } async run(fn: () => Promise): Promise { await this.acquire(); try { return await fn(); } finally { this.release(); } } }