@peerfold/js
v1.2.0
Published
Prebuilt browser bundle for Peerfold — exposes window.Peerfold (client factory + token storage, learner-token exchange, and progress beacon helpers). Loadable via a <script> tag or as ESM.
Maintainers
Readme
@peerfold/js
Prebuilt browser bundle for Peerfold. Exposes window.Peerfold for
<script>-tag use (this is what the future Cloud-connected HubSpot theme loads)
and is also importable as ESM. Wraps @peerfold/api-client and
adds a few tiny, DOM-free helpers: a token-storage adapter, a learner-token
exchange call (PRD Flow 1 relay), and a progress beacon.
Install
For bundled apps:
npm install @peerfold/jsFor a no-build page, load the hosted bundle — no install, no npm:
<script src="https://app.hublms.com/sdk/peerfold.js"></script>
<script>
const { client, refresh } = Peerfold.createPeerfold({ baseUrl: "https://acme.site.hublms.com" });
</script>A note on naming (1.0.0). Everything is Peerfold-named: the global
window.Peerfold, the factorycreatePeerfold, the classesPeerfoldClient/PeerfoldError, the/sdk/peerfold.jsand/sdk/cart/peerfold-cart.jsURLs, theX-Peerfold-*/Peerfold-Versionheaders and thepeerfold.*localStorage keys. 0.1.x used the oldHubLMS-prefixed names; 0.2.0 renamed the import surface while the API kept answering the old wire spellings, so a page already running 0.1.1 was not broken mid-flight. 1.0.0 closes that window — the API no longer emitsHubLMS-Versionand no longer readsX-HubLMS-Publishable-Key. Pin^1.0.0.
| 0.1.x wire name | 1.0 name | Direction |
|---|---|---|
| HubLMS-Version | Peerfold-Version | response (and the request-side version pin) |
| X-HubLMS-Publishable-Key | X-Peerfold-Publishable-Key | request |
If a <script src=".../sdk/peerfold.js"> page suddenly gets 401 unauthorized
on a catalog read after this release, it is sending the old key header: upgrade
the bundle, or send X-Peerfold-Publishable-Key.
Build
dist/ is gitignored — build it locally or in CI:
pnpm --filter @peerfold/js build
# → dist/peerfold.js (IIFE, exposes window.Peerfold) ~12.5 KiB min
# → dist/peerfold.esm.js (ESM) ~12.0 KiB minThe bundle is fully self-contained (zero runtime dependencies): the client's
only compile-time imports are import type and erase in the build.
Script-tag use
<script src="/path/to/peerfold.js"></script>
<script>
const { client, refresh } = Peerfold.createPeerfold({
baseUrl: "https://acme.site.hublms.com",
relayUrl: "/_hcms/api/peerfold-token", // portal-local serverless relay (holds the sk)
});
// On load: mint a learner token from the relay, then read the catalog.
refresh().then(async () => {
const catalog = await client.catalog.list();
render(catalog.data);
});
</script>createPeerfold wires the client's learner token to a TokenStorage (localStorage
by default), so every request reads the current token and refresh() re-mints it
via the relay.
ESM use
import { createClient, exchangeLearnerToken, progressBeacon, localStorageTokenStorage } from "@peerfold/js";@peerfold/js re-exports the entire @peerfold/api-client surface, plus:
Token storage
const store = Peerfold.localStorageTokenStorage(); // SSR-safe: falls back to in-memory
store.set(token);
store.get();Learner-token exchange (Flow 1 relay)
// POSTs to a portal-local relay that asserts the logged-in member server-side
// and returns a short-lived learner token. The browser never sees a secret key.
const { access_token } = await Peerfold.exchangeLearnerToken("/_hcms/api/peerfold-token");Progress beacon
// Reliable end-of-visit progress write — survives page unload via
// fetch(keepalive), carrying Authorization + Idempotency-Key (which
// navigator.sendBeacon cannot set).
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") {
Peerfold.progressBeacon("https://acme.site.hublms.com", enrollmentId, {
token: store.get(),
event: { event_id: crypto.randomUUID(), type: "lesson_viewed", lesson_id: lessonId },
});
}
});Cart drawer (dist/peerfold-cart.js)
A second, independent entry point built from the same package: the embeddable
cart drawer for selling courses from a merchant's OWN website (E15 chunk 2). It
shares nothing with the SDK bundle above — no @peerfold/api-client, no shared
runtime — because it is pasted onto marketing pages where every kilobyte counts.
The build enforces a 15 KB gzipped budget and FAILS over it.
<script
src="https://app.hublms.com/sdk/cart/peerfold-cart.js"
data-peerfold-cart
data-key="pk_live_…"
defer
></script>
<button data-peerfold-add-to-cart="COURSE_ID">Add to cart</button>It exposes window.PeerfoldCart (add, remove, open, close, refresh).
It also paints two things on the merchant's own page: any
data-peerfold-open-cart element becomes the Peerfold pill with a count chip,
and any data-peerfold-order-status element shows what a returning buyer's
checkout actually did. The styles are injected, and they read the --pf-*
variables of the Peerfold Embed design system with the system's own defaults as
fallbacks, so the drawer looks finished on a bare page and takes the brand on a
themed one. Full instructions — including the CORS/return-URL allowlist
requirement and what happens after checkout — are in docs/embed-cart.md.
