Skip to content
CDNPulse

/ sources

Two sources, one grammar

Everything CDNPulse serves comes from a public package published to the npm registry, or from a file attached to a tagged release of a public repository. Nothing is uploaded by hand and nothing is mirrored on request.

Path grammar

Read left to right: prefix, identity, version, path inside the artefact.

/npm/<pkg>@<ver>/<file>
A file from a published npm tarball. Scoped names keep their slash: /npm/@scope/name@1.2.3/index.js.
/npm/<pkg>@<ver>/
Directory index for that version. Works at any depth, not only the root.
/npm/<pkg>@<ver>/+mod
The package's browser entry point, rewritten so that bare imports point at absolute CDNPulse URLs. Appendable to a specific file too.
/rel/<owner>/<repo>@<tag>/<file>
A file from the source tree at that release tag. Tags are taken verbatim, including a leading v.
/rel/<owner>/<repo>@<tag>/~assets/<file>
A binary asset attached to that release rather than committed to the tree.
/join/<member>,<member>
Concatenation of two to twenty members, each written without the leading slash. All members must share a media type.
/meta/<pkg>
JSON list of known versions and dist-tags for a package. Cached for twelve hours, CORS open.

Case and encoding. Paths are case sensitive and are matched against the artefact exactly as published. Percent-encode anything outside the unreserved set; a raw space or a raw # will be read as a URL delimiter, not as part of a filename.

Version ranges

Anything that is not an exact version produces a redirect rather than a body. The redirect is what expires; the target it points at never does.

Written asResolves toResponseCached for
@5.3.6that exact release2001 year, immutable
@5.3newest 5.3.x30212 hours
@5newest 5.x.x30212 hours
@latestthe registry's default dist-tag3026 hours
@nextany other published dist-tag3021 hour
(omitted)same as @latest3026 hours

Pre-release identifiers are never picked up by a floating range. Ask for them by exact version or by dist-tag.

Transforms

A transform is derived from a file that really exists in the artefact. It is computed once, stored next to the original, and served with its own digest.

Minify
Request name.min.js or name.min.css where only the unminified file was published. Comments and whitespace go; identifiers in the public surface are left alone.
Module rewrite
Suffix /+mod turns bare specifiers into absolute CDNPulse URLs and pins each dependency to the version the package declared.
Compression
Brotli first, gzip as fallback, chosen from Accept-Encoding. Already-compressed media is passed through untouched.
Join
Members are concatenated in the order written, separated by a single newline, and the result is cached as one immutable object.

What is never transformed. If you request a file by its published name, you get the published bytes. Minification only happens for a name that does not exist upstream, so a digest you calculated from the tarball will always match.

Published digests

SHA-384, base64, one per immutable path. A digest for any other path is returned in the Digest response header.

/npm/bootstrap@5.3.6/dist/css/bootstrap.min.css
sha384-Quy3qFNAXKVeFvPlA371+Gv39nwDocVgDq6Jtjm+tvDMMvgx1kQ3Eev9Ro0eTCGF

/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css
sha384-04Bg1uNuxOJcFuefgxXtR2R1pff/rDXejL4lGMoYgYuWbfMx+c0Kmp6rePq4RLW2

/npm/bootstrap@5.3.6/dist/js/bootstrap.bundle.min.js
sha384-miH03ZE/VYPolx8f0d5QBQwyOaSLcosI1caOCT23ZSSyPrV3JLNVW27Xfjz490GR

/npm/jquery@3.7.1/dist/jquery.min.js
sha384-qAKdY1KBrKUEIA2RsUxEDSVKytRnG8CFKcFHA69aXAvkCVr6XL0TZ0o/ZFZApVQ0

/npm/chart.js@4.4.9/dist/chart.umd.js
sha384-qSfY9vL/Nx9h2ZaNMlrjOSHZZdqj4BLAwUOc3duoRWEGwoQsuaT3dmoMDrMVH2ex

/npm/swiper@11.2.6/swiper-bundle.min.css
sha384-Kv8wuS7yEqvoPG4Sv+QQh9mopIYoASFItCI0N2d0WASYHDYwrpW5u0flpBPLcfRZ

/npm/axios@1.9.0/dist/axios.min.js
sha384-vU4ncMaFch4LBPzR/8s8X7MdHp+c/YHevLDIp3TRBDlyYNjHlsxCVbS8A9cWyD8p

/npm/vue@3.5.14/dist/vue.global.prod.js
sha384-TMCujkl0IRbbSlEUCSf6jfd5qNLV6iLKZmzb2qLdC9/drB9/26iCWNEHc8kUiCio

/npm/dayjs@1.11.13/dayjs.min.js
sha384-Amn8xyITKGI514wvAY6xSqlbzqVRq0LuBeRk8GtlYG6H2nrTcfUUYkxJwg3rbJWW

/npm/highlight.js@11.11.1/styles/github-dark.min.css
sha384-0SNRXriHaBV+j/R5zF7qeOZCb6IsFQqCUHoXyXgt5U/6gjOIn/ZTLHjvo+WaV+cp

Using a digest
<script src="https://cdnpulse.org/npm/jquery@3.7.1/dist/jquery.min.js"
        integrity="sha384-qAKdY1KBrKUEIA2RsUxEDSVKytRnG8CFKcFHA69aXAvkCVr6XL0TZ0o/ZFZApVQ0"
        crossorigin="anonymous"></script>

Limits

File size
50 MB per object. Larger files are proxied but not held at the edge.
Join members
20, and 6 MB for the combined result.
New version lag
A freshly published release is usually reachable within 90 seconds and always within 15 minutes.
Per-client rate
None for ordinary browser traffic. Automated pulls that sustain more than 400 requests per second from one address are shaped, not blocked.
Unpublished versions
Kept and served. A version that disappears upstream keeps answering here so existing pages do not break.

Not a package manager. CDNPulse answers plain HTTP GET requests for individual files. It does not implement a registry API, does not accept publishes, and cannot be pointed at by an install client as a replacement registry.