npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@generazioneai/jobs

v0.1.0

Published

Cron ai servizi proprietari: @Job con lock consultivo di transazione nel database del servizio (una sola esecuzione per giro fra le repliche), registro cron_runs, JOBS_ENABLE/JOBS_DISABLE e metriche OpenTelemetry (spec «Fondamenta enterprise» §2.4).

Readme

@generazioneai/jobs

Cron ai servizi proprietari (spec «Fondamenta enterprise» §2.4, decisione 12). Un metodo con @Job gira in una sola replica per giro, con il lock nel database del servizio (niente Redis), e ogni giro finisce in cron_runs.

npm i @generazioneai/jobs
// app.module.ts
JobsModule.forRoot({ prisma: PrismaService })

// billing-jobs.service.ts
@Injectable()
export class BillingJobs {
  @Job('billing.expireEntitlements', '23 * * * *', { tz: 'UTC', lockTtl: '5m' })
  async expire() { /* ... */ }

  // spostato dal gateway: parte solo se il nome è in JOBS_ENABLE
  @Job('billing.reconcilePending', '41 * * * *', { defaultEnabled: false })
  async reconcile() { /* ... */ }
}

Come garantisce una sola esecuzione

  1. Il giro apre una transazione e prende pg_try_advisory_xact_lock(hashtext('genai.jobs'), hashtext(<nome>)). Chi non lo prende salta (locked). La transazione resta aperta per tutto il giro con timeout = lockTtl: se il job la supera, il lock decade (il giro si segna lock_expired).
  2. Con il lock preso, se cron_runs.last_run_at è già dentro lo slot del tick (minuto, o secondo con espressioni a sei campi) il giro è già stato fatto da un'altra replica e si salta (already_ran): un job rapido non gira due volte perché la seconda replica scatta qualche millisecondo dopo.
  3. running_since e last_run_at si scrivono fuori dalla transazione (autocommit): la salute vede il giro mentre gira.

Un giro tiene due connessioni (la transazione del lock e le scritture del registro): il pool di Prisma deve averne almeno quante i job che possono girare insieme, più quelle del job. Senza la tabella cron_runs i job girano lo stesso (avviso nel log) ma non c'è la guardia «stesso giro», solo il lock.

Accensione

  • JOBS_ENABLE / JOBS_DISABLE: liste di nomi separate da virgola; billing.* = prefisso, * = tutti. JOBS_DISABLE vince. Di serie conta defaultEnabled (vero se non dichiarato).
  • G2: i job spostati dal gateway escono con defaultEnabled: false; si accendono con JOBS_ENABLE nello stesso giro in cui GATEWAY_CRON_DISABLE li spegne nel gateway.
  • Un job spento resta lanciabile a mano (JobsService.runNow(name), che ignora anche la guardia «stesso giro» ma non il lock).

cron_runs

HR e Train hanno già la tabella (name, last_run_at, last_ok_at, last_error, consecutive_failures, running_since, instance): la libreria scrive quelle colonne e usa le colonne aggiunte solo se esistono (schedule, last_duration_ms, runs_total, last_status). Per un database nuovo (Bill, AI, Notify) o per aggiungerle: sql/cron_runs.sql (idempotente, solo additiva, con il rientro in testa) oppure ensureSchema: true / JOBS_ENSURE_SCHEMA=true. In stg e produzione la applica il registro delle SQL del workspace, prima del codice. I tempi si scrivono con now() del database: nelle colonne timestamp(3) di HR valgono le stesse regole del suo CronRunStore (sessione in UTC).

Metriche (API di OpenTelemetry, esportate da @generazioneai/telemetry)

job_runs_total{job,status=ok|error|skipped}, job_duration_seconds{job}, job_last_success_timestamp{job} (da cron_runs.last_ok_at: vale per tutte le repliche).

Prove

JOBS_TEST_DATABASE_URL=postgresql://… npm test (senza, le prove su Postgres si saltano). Esempio locale: docker run -d -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=jobs_test -p 55432:5432 postgres:18.