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

galletas4all

v1.0.3

Published

Lightweight, self-hosted cookie consent widget for Chilean nDPL compliance. Logs consent to your own Cloudflare D1 database.

Readme

Galletas4all

A lightweight cookie consent widget for the Chilean nDPL (Ley 21.719) and GDPR. About 10 KB minified. Self-hosted on Cloudflare D1 — no third-party trackers, no SaaS fees, no shared database.

npm version CI License: MIT

Live demo: https://galletas4all.pages.dev · Docs: ARCHITECTURE.md · Security: SECURITY.md

Why this exists

Chile's new Personal Data Protection Law (Ley 21.719) takes effect in late 2026. Most cookie consent SaaS tools charge per-site fees, share a tracker across every customer, or both. For small Chilean sites, that's the wrong shape of solution.

Galletas4all is what I would use on my own sites: a small widget that asks for consent honestly, blocks third-party scripts until the visitor agrees, and writes the audit log to a D1 table I own. Cloudflare's free tier covers the math for sites well into the tens of thousands of visitors per day.

Quick start — script tag (zero build step)

<script
  defer
  src="https://cdn.jsdelivr.net/npm/[email protected]/dist/widget.js"
  integrity="sha384-xth1tc/hMyIJV6UohIWoDvGBZbYC7WcQlwGVQPjCxIfBR8VL/EJhmWLeRoYMalzD"
  crossorigin="anonymous"
  data-api-url="https://your-worker.workers.dev"
  data-lang="es"
></script>

About integrity: the SRI hash pins the file. If the CDN is ever compromised, your browser refuses to execute the tampered bundle. The trade-off is that the snippet is version-locked — bumping to a new release means copying the new hash from the release notes (published on every GitHub release).

If you prefer auto-updates over SRI, drop both attributes and use @1 (moving tag). For a consent/compliance widget, we recommend keeping SRI on.

Mark any third-party script you want to gate:

<script src="https://www.googletagmanager.com/gtag/js?id=G-XXX" data-consent-category="analytics"></script>

The widget blocks tagged scripts until the visitor accepts the matching category, then swaps them back in.

Quick start — npm

npm install galletas4all
import { mount, onConsentChange } from 'galletas4all';

const teardown = mount({ apiUrl: 'https://your-worker.workers.dev' });

onConsentChange((record) => {
  console.log('User decided:', record);
});

mount() returns a teardown function. Call it on SPA route teardown to disconnect the script-blocking observer.

Deploy the Worker

git clone https://github.com/felipemillan/Galletas4all
cd Galletas4all/worker
cp wrangler.toml.example wrangler.toml
# edit wrangler.toml: set database_id and ALLOWED_ORIGIN
npx wrangler d1 create galletas4all-db
npx wrangler d1 execute galletas4all-db --remote --file=./schema.sql
npx wrangler deploy

ALLOWED_ORIGIN is a comma-separated list of sites permitted to POST consent logs. Set * only for local development.

Optional: enable the GET /stats endpoint with npx wrangler secret put STATS_TOKEN. Without STATS_TOKEN set, /stats returns 503 (default-deny — prevents accidental exposure of consent volume).

What the widget actually does

| Step | Where | Notes | |---|---|---| | 1. Show banner | Browser | Reads localStorage first — no banner if already decided. | | 2. Block scripts | Browser | MutationObserver rewrites type="text/plain" until consent. | | 3. Persist decision | Browser | localStorage["galletas4all_consent"]. | | 4. Send audit log | Browser → Worker | navigator.sendBeacon first; fetch({keepalive:true}) fallback. | | 5. Write to D1 | Worker | One row per decision. Parameterized insert. |

Full data-flow diagrams in ARCHITECTURE.md.

Configuration

| Attribute | Default | What | |---|---|---| | data-api-url | "" (local-only mode) | URL of your Worker. Omit to skip audit logging. | | data-lang | auto-detected | "es" or "en". Otherwise from ?lang=, then navigator.language. | | data-policy-version | "" | Optional version label of your cookie policy (e.g. "2026-06"). Stamped onto each consent log so you can prove which policy text the visitor saw. | | data-consent-category on a <script> | — | "essential", "analytics", or "marketing". Untagged scripts are not blocked. |

Worker env vars: ALLOWED_ORIGIN (required for production), STATS_TOKEN (required to enable GET /stats).

Browser support

Modern Chromium, Firefox, Safari (incl. iOS), Edge. Uses MutationObserver, navigator.sendBeacon, crypto.randomUUID with a getRandomValues fallback.

Limitations

  • Audit rows are admin-mutable. Anyone with D1 access can edit or delete rows. There is no tamper-proofing or signed ledger yet — that is planned for v2.0 (see docs/ROADMAP.md). For now, treat the D1 log as an operational audit trail, not a cryptographically verifiable record.

  • visitor_id lives in localStorage. The key is galletas4all_vid. If the visitor clears their browser storage, uses a different device, or opens a private/incognito window, they get a new ID. The link between new and old consents is lost. There is no cross-device or cross-browser identity here by design (that would require account login, which is out of scope).

  • The Origin header is spoofable. The Worker uses the Origin header to enforce the ALLOWED_ORIGIN allow-list. A non-browser client can forge that header. The allow-list reduces casual abuse; it does not stop a determined attacker. There is also no rate limiting on the Worker. Don't rely on ALLOWED_ORIGIN as a security boundary — it is a footgun-prevention measure.

DSAR runbook

Chilean Ley 21.719 (and GDPR Art. 15–17) gives visitors the right to access and erase their data. Here is how to handle both requests.

Step 1 — The visitor finds their ID.

Ask the requester to open their browser console on your site and run:

localStorage.getItem('galletas4all_vid')

The value looks like vid_.... That is the visitor_id you will query.

Step 2 — Access request (deliver the data).

Run against your D1 database and deliver the resulting rows as JSON (or a CSV export):

npx wrangler d1 execute galletas4all-db --remote \
  --command "SELECT * FROM consent_logs WHERE visitor_id = 'vid_...';"

Replace vid_... with the actual value the visitor reported. The --remote flag is required — without it, wrangler queries your local dev database, not production D1, and you will get no rows.

Step 3 — Erasure request (right to be forgotten).

npx wrangler d1 execute galletas4all-db --remote --yes \
  --command "DELETE FROM consent_logs WHERE visitor_id = 'vid_...';"

The --remote flag is required — without it the DELETE runs against your local dev database and never touches production data. --yes skips the interactive confirmation for this destructive command; omit it if you want to confirm the production write manually.

Confirm the delete returned at least one row. If it returned zero, the ID was not in the production database (visitor may have already been purged, or they used a different browser).

For scheduled bulk purges based on age, see docs/RETENTION.md.

Contributing

Issues and PRs welcome. Read SECURITY.md before reporting vulnerabilities (use email, not the issue tracker).

The codebase is small and TypeScript-strict. To work locally:

npm install
npm test        # vitest + jsdom + Miniflare
npm run build   # IIFE + ESM + .d.ts
npm run dev     # serve index.html on :3000

License

MIT — see LICENSE.


Versión en español

Widget de consentimiento de cookies para la nueva Ley 21.719 de Chile y el GDPR europeo. ~10 KB minificado. Auto-alojado en Cloudflare D1: sin trackers de terceros, sin tarifas SaaS, sin base de datos compartida.

Demo en vivo: https://galletas4all.pages.dev · Arquitectura: ARCHITECTURE.md

Uso rápido — etiqueta script

<script
  defer
  src="https://cdn.jsdelivr.net/npm/[email protected]/dist/widget.js"
  integrity="sha384-xth1tc/hMyIJV6UohIWoDvGBZbYC7WcQlwGVQPjCxIfBR8VL/EJhmWLeRoYMalzD"
  crossorigin="anonymous"
  data-api-url="https://tu-worker.workers.dev"
  data-lang="es"
></script>

El atributo integrity (SRI) ancla el archivo. Si el CDN llegara a estar comprometido, el navegador rechaza el código alterado. A cambio, debes actualizar el hash al cambiar de versión — lo publicamos en cada release de GitHub.

Marca cualquier script de terceros que quieras controlar:

<script src="..." data-consent-category="analytics"></script>

El widget bloquea los scripts marcados hasta que el visitante acepte la categoría correspondiente.

Despliegue del Worker

Mismas instrucciones que la sección en inglés. Necesitas una cuenta gratuita de Cloudflare. El esquema D1 es idempotente.