@fkn/lib is one npm package, and a web app adds it with one install. A bundle that carries the socket surface also needs three Node shims. This page covers what the package ships, which import path to take, the three shims your bundler has to supply, the optional React peer, the one runtime requirement, and the headers for a cross-origin isolated page.
Every import path ships three files: an ESM .js, a CJS .cjs and one bundled .d.ts. There are 28 of them, plus the manifest at @fkn/lib/package.json for tooling that reads the version. entry points lists every one and what it gives you.
osra, ip-address and @types/node arrive as dependencies. @types/node is a runtime dependency rather than a dev one, because the public net, dgram, http and fs declarations reference Node’s builtin types.
The broker is the connection your app holds into FKN, and every entry that reaches it mounts a hidden fkn.app iframe, the broker frame, or adopts one the page already holds, as soon as it evaluates in a window. The library appends a new frame to document.body, so the body has to exist by then. The exact conditions are on how it works.
The root entry gathers most of the library under one import, and each subpath carries one piece of it. The two spellings are one implementation: cloud.fetch from the root and fetch from @fkn/lib/cloud/fetch are the same function. Six subpaths stay separate, because the root does not re-export them: @fkn/lib/api, @fkn/lib/react, @fkn/lib/contract, @fkn/lib/wire, @fkn/lib/messages and @fkn/lib/attach-policy, all listed on entry points.
What differs is what the published files pull in. @fkn/lib/cloud/fetch reaches only osra. The root also pulls buffer, events and stream, three Node builtins a browser bundle has to shim, because the socket surface is built on Node’s stream and event classes, and the file and datagram surfaces hand back a Buffer. An app that fetches and nothing more can take the narrow path and skip the shims:
The request went through the proxy, the server that cloud.fetch sends a request through, and no socket code came along. What the proxy answers is on fetch().
The narrow path carries less code, and it still mounts the broker frame exactly as the root does. Take the root when the app uses several surfaces, since the shims below cover all of it.
osra, ip-address and react are packages your bundler resolves on its own, and the three that remain are the shims. So net needs events and stream, dgram needs buffer and events, http needs all three, and fs needs buffer. A browser has none of them, so the bundle has to provide each name before the entry that imports it evaluates. The stream shim also reads global at module scope, so the globals matter as much as the modules.
With Vite, vite-plugin-node-polyfills scoped to exactly those three does both, and it defines global, process and Buffer by default. Add it as a dev dependency and list the three names:
Includes specific modules. If empty, includes all modules
@example
nodePolyfills({
include: ['fs', 'path'],
})
include: ['buffer', 'events', 'stream'],
protocolImports?: boolean |undefined
Specify whether the Node protocol version of an import (e.g. node:buffer) should be polyfilled too.
@default ― true
protocolImports: false }), // a build in which @fkn/lib/net resolves its bare stream import
],
})
That is the configuration this site builds with: three names and nothing else. protocolImports: false is enough for the library, whose published files import the three names bare. Set it to true when your own code imports node:buffer, node:events or node:stream.
@fkn/vite-plugin supplies the same shims and aliases the Node names to the matching entries, so code written for Node keeps its imports. With another bundler, alias the same three names to browser implementations and define the same three globals. The production build is on bundle an app that uses sockets.
react is an optional peer at >=18, and only @fkn/lib/react imports it. Installing @fkn/lib does not pull React in. Install React only when you render ConnectButton, the one component the package exports, which is described under account and quota.
A realm is one JavaScript execution context, such as a window or a worker. @fkn/lib needs one of two: a window realm, where it mounts the broker frame and talks to the broker through it, or a worker the page relayed.
A worker is a realm of its own, and it cannot open a broker connection by itself, so the page relays its calls. Inside a worker there is no document to mount the frame in, so the worker reaches the broker only once the page awaits relayWorker. What the relay does, its options, and how it ends are on workers.
After that the worker imports @fkn/lib/net exactly as the page does. See run sockets in a worker for the pair, and workers for what works inside one. A test runner has no broker to reach, and what runs without one is on testing.
A page is cross-origin isolated when it is served with Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp, which is what unlocks SharedArrayBuffer. Such a page can embed only a frame whose response opts in, so skip this section unless the page needs isolation.
On an isolated page @fkn/lib mounts /api?coi=1 instead of /api, a variant of the broker document that carries the headers an isolated page can embed, and it sets allow="cross-origin-isolated" on the frame so the isolation is handed down. The library never falls back to the plain /api frame, by design: an isolated page blocks that frame, so a fallback would hang. Serve the two headers from your dev server and your host alike, where server covers vite dev, preview covers vite preview, and a production host sends the same two on the document: