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

@block65/csp

v0.2.0

Published

Composable Content-Security-Policy builder with presets and deterministic output

Readme

@block65/csp

Content-Security-Policy builder: policies are plain objects, composeCsp merges them and serialises the header. Node 26+, browsers with Uint8Array base64, Cloudflare Workers.

Install

pnpm add @block65/csp

Usage

composeCsp(...parts) unions each directive across parts, deduped and sorted.

import { composeCsp, sameOrigin, sentrySdk, strict } from "@block65/csp";

composeCsp(
	strict(),
	sameOrigin(),
	sentrySdk({ dsn: "https://[email protected]/1", replay: true }),
);
// base-uri 'none'; connect-src 'self' https://o123.ingest.us.sentry.io; …

Presets

Each returns Directives.

  • strict()
  • sameOrigin()
  • googleFonts()
  • googleMaps({ nonce: string | true }) or googleMaps({ nonce: false, allowUnsafeScripts: true })
  • sentrySdk({ dsn: string, replay?: boolean })
  • stripe({ paymentFields?: boolean, cardChallenges?: boolean, checkout?: boolean })
  • cloudflareTurnstile()
  • cloudflareInsights()
  • maplibre({ tiles: readonly string[], worker: "blob" | { url: string } })
  • viteDevServer() — empty

A string nonce puts the nonce on googleMaps script-src and style-src, and 'strict-dynamic' on script-src. Non-nonce mode requires googleMaps({ nonce: false, allowUnsafeScripts: true }) to acknowledge 'unsafe-inline' and 'unsafe-eval' permissions across the composed policy. This is enforced by both TypeScript and runtime checks.

'none'

Dropped from any directive that also has a real source.

composeCsp({ scriptSrc: ["'none'"] }, { scriptSrc: ["https://example.com"] });
// script-src https://example.com

remove

Deletes a directive an earlier part set; a later part can set it again.

import { composeCsp, remove, strict } from "@block65/csp";

composeCsp(strict(), {
	upgradeInsecureRequests: remove,
	frameAncestors: remove,
});

MapLibre worker variants

Choose the mode that matches the MapLibre build and worker configuration:

// A build that creates blob workers.
maplibre({ tiles: ["https://tiles.example.com"], worker: "blob" });

// A same-origin worker configured with setWorkerUrl in the application.
maplibre({
	tiles: ["https://tiles.example.com"],
	worker: { url: "https://app.example.com/maplibre-gl-worker.mjs" },
});

The worker choice is required. Neither variant adds child-src, so it does not also permit blob frames. sameOrigin() adds 'self' to worker-src; omit or remove that directive before composing URL mode if you want only the named worker. CSP path restrictions do not constrain a redirect destination's path.

Nonces

generateNonce(byteLength = 16) per response, nonce(value) for the source.

import {
	composeCsp,
	generateNonce,
	nonce,
	sameOrigin,
	strict,
} from "@block65/csp";

const value = generateNonce();
const policy = composeCsp(strict(), sameOrigin(), {
	scriptSrc: [nonce(value)],
});

Hashes

hash(content, algorithm = "SHA-256") takes the element's exact text content.

import { composeCsp, hash } from "@block65/csp";

composeCsp({ scriptSrc: [await hash('console.log("hi")')] });
// script-src 'sha256-…'

Reporting

reportTo names an endpoint from the Reporting-Endpoints header; reportUri covers browsers without report-to.

import { buildReportingEndpoints, composeCsp, strict } from "@block65/csp";

const endpoint = "https://example.com/csp-reports";

new Response(body, {
	headers: {
		"reporting-endpoints": buildReportingEndpoints({ csp: endpoint }),
		"content-security-policy": composeCsp(strict(), {
			reportTo: "csp",
			reportUri: endpoint,
		}),
	},
});

Send the policy as content-security-policy-report-only to collect violations without enforcing it.

Documentation audit

Reviewed against the CSP specifications and vendor documentation on 2026-09-16. Every directive in lib/types.ts and every preset has a documentation URL in its JSDoc. The presets are additive building blocks; compose sameOrigin() when local resources are needed.

| Preset | Reference and scope | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | strict() | CSP3: blocks resource loads, framing, forms and base URL changes; does not enable sandbox. | | sameOrigin() | Source lists: allows the listed resource types from the document origin. | | googleFonts() | Google Fonts: hosted CSS and font files. | | googleMaps() | Maps CSP: nonce mode deliberately omits the example's unsafe-eval and legacy script fallbacks. Nonce the initial script and a style element; nonce: true requires supplying both policy nonces yourself. | | sentrySdk() | Sentry CSP: bundled SDK and DSN ingestion; CDN loaders and tunnels need separate sources. | | stripe() | Stripe CSP: Stripe.js and optional Checkout. Address Element with your own Maps key, Link, custom font stylesheets, Connect and crypto have additional requirements. | | cloudflareTurnstile() | Turnstile CSP: pre-clearance also needs connect-src 'self'. | | cloudflareInsights() | Web Analytics CSP: manual beacon; automatic injection also needs connect-src 'self'. | | maplibre() | MapLibre CSP: explicit blob or URL worker mode. URL mode allows only the supplied script path. Include style, tile, glyph and sprite fetch origins in tiles. | | viteDevServer() | Vite CSP: an empty extension point. Configure nonces, HMR connections and asset sources in the application. |

strict-dynamic affects the whole composed script policy: parser-inserted scripts need nonces even when another preset allows their host. Explicit scriptSrcElem or styleSrcElem directives also override the corresponding general directive.

Broad permissions that remain are intentional: Google's service wildcards follow its Maps guide, and Stripe's *.js.stripe.com supports dynamically assigned frame origins. Checkout's image wildcard follows Stripe's requirements and is only added with checkout: true. Cloudflare Analytics is restricted to the beacon script and ingestion paths. Blob worker permission requires an explicit choice in MapLibre; Sentry adds it only with replay: true. Google Maps includes it as part of its documented renderer requirements.

This review verifies documented rules, not live integration behavior. Source validation prevents header delimiter injection; it is not a complete CSP grammar or browser compatibility validator. Test the composed policy with the SDK versions and features your application uses.

License

MIT