@ferscloud/fers-calculation-web
v0.2.71
Published
Structural analysis FEM solver for beams, frames and trusses, compiled from Rust to WebAssembly. Runs in the browser or in Node with no server round-trip. Includes Eurocode EN 1993-1-1 steel checks.
Maintainers
Readme
@ferscloud/fers-calculation
High-performance structural engineering FEM solver compiled from Rust to WebAssembly. Runs entirely in the browser — no server round-trip per calculation.
This README covers both published packages:
@ferscloud/fers-calculation(Node.js / server-side,--target nodejs) and@ferscloud/fers-calculation-web(browser / bundler,--target bundler). The API is identical; only installation and bundler setup differ.
Installation
# Browser apps (Vite, Next.js, webpack):
npm install @ferscloud/fers-calculation-web
# Node.js / server-side:
npm install @ferscloud/fers-calculationBundler setup (browser build)
@ferscloud/fers-calculation-web is a WASM ES module that initialises via top-level await. Most bundlers need a one-time config:
Vite
npm i -D vite-plugin-wasm// vite.config.ts
import wasm from "vite-plugin-wasm";
// The package initialises with a top-level await, so the build target has to
// be one that supports it. `esnext` needs no second plugin.
export default {
plugins: [wasm()],
build: { target: "esnext" },
};esnext keeps the top-level await in the output, which needs Chrome, Edge or
Firefox 89+, or Safari 15+. If you still have vite-plugin-top-level-await in
your config from an earlier version of this README, drop it — recent
@swc/core releases make it fail at vite build.
Next.js / webpack
// next.config.js
module.exports = {
transpilePackages: ["@ferscloud/fers-calculation-web"],
webpack(config) {
config.experiments = { ...config.experiments, asyncWebAssembly: true };
config.output.environment = { ...config.output.environment, asyncFunction: true };
return config;
},
};The solver is synchronous and CPU-bound; for large models run it inside a Web Worker so the UI thread stays responsive. The Node.js package (@ferscloud/fers-calculation) needs no bundler config.
Quick start — free tier (non-commercial, up to 100 members)
import { calculate_from_json } from "@ferscloud/fers-calculation";
// No init() call needed — the nodejs and bundler builds initialise the
// WASM module automatically on import.
const res = JSON.parse(calculate_from_json(JSON.stringify(myModel)));
if (res.ok) {
const data = res.result; // displacements, member_results, unity_checks, …
} else {
console.error(res.error.code, res.error.message);
}No API key required. Works for any model with up to 100 members.
Response shape
Every solver call returns a JSON envelope, so you JSON.parse once and branch on ok — you never have to sniff whether the returned string "looks like" an error:
// success
{ "ok": true, "result": { /* the full result document */ } }
// failure (invalid model, over the member limit, malformed JSON, …)
{ "ok": false, "error": { "code": "LimitExceeded",
"message": "Number of members (250) exceeds allowed maximum of 100" } }error.code is one of InvalidJson, LimitExceeded, SolveError, InternalPanic, InternalSerialization. The return value is always valid JSON.
Authenticated tier — Pro (up to 10 000 members)
Pro limits are unlocked by passing a short-lived signed token issued by the FERS Cloud server. The token is verified inside the WASM using an Ed25519 public key baked into the binary — it cannot be forged.
How it works
Your server → POST https://ferscloud.com/api/solver/token → signed 30-min token
(X-API-Key: <your-api-key>)
Browser → calculate_from_json_with_token(model, token) → Pro limits unlocked1. Get an API key
Log in at ferscloud.com, go to Profile → API Keys, and create a key. Store it as an environment variable on your server — never in browser code.
2. Backend: fetch a solve token
Your server fetches and caches the token. Example for a Next.js API route:
// pages/api/solve-token.ts
const FERS_API_KEY = process.env.FERS_API_KEY!;
let cached: { token: string; expiresAt: number } | null = null;
export default async function handler(req, res) {
const now = Date.now();
// Re-use if more than 5 minutes remain
if (cached && cached.expiresAt - now > 5 * 60 * 1000) {
return res.json({ token: cached.token });
}
const resp = await fetch("https://ferscloud.com/api/solver/token", {
method: "POST",
headers: { "X-API-Key": FERS_API_KEY },
});
if (!resp.ok) return res.status(502).json({ error: "Token fetch failed" });
const { token, expiresAt } = await resp.json();
cached = { token, expiresAt: new Date(expiresAt).getTime() };
return res.json({ token });
}Same pattern works for Express, Deno, Bun, Cloudflare Workers, or any server-side runtime.
3. Frontend: call the solver with the token
import { calculate_from_json_with_token } from "@ferscloud/fers-calculation";
const { token } = await fetch("/api/solve-token").then(r => r.json());
const res = JSON.parse(calculate_from_json_with_token(JSON.stringify(myModel), token));
if (!res.ok) throw new Error(`${res.error.code}: ${res.error.message}`);
const data = res.result;If the token is missing, expired, or invalid, the solver falls back to the free 100-member limit — it never throws (a genuine solve failure comes back as { ok: false, error }, not an exception).
Tier comparison
| Feature | Free | Pro |
|---|---|---|
| Use | Non-commercial | Commercial and non-commercial |
| Max members | 100 | 10 000 |
| WASM function | calculate_from_json | calculate_from_json_with_token |
| Requires token | No | Yes |
| Token TTL | — | 30 minutes |
Security note
Keep your FERS_API_KEY server-side only. The solve token it fetches is safe to pass to the browser — it is short-lived and cryptographically signed. Only FERS Cloud can issue valid tokens; the browser WASM can only verify them.
TypeScript types
The browser package ships generated model types alongside the function signatures:
import type { FERS, ResultsBundle } from "@ferscloud/fers-calculation-web/fers-models";FERS is the input model; ResultsBundle is the result payload of a successful envelope. Both are generated from the engine's OpenAPI schema, so they track the published version.
Deflected shape (optional)
Set include_member_deflected_shape in the model's analysis options to get a
ready-to-plot, load-exact deflected shape per member — the member's global
displacement sampled along its length — instead of reconstructing the curve yourself:
const model = {
/* … model + load cases … */
analysis: { /* … */ options: { /* … */ include_member_deflected_shape: true } },
};
const res = JSON.parse(calculate_from_json(JSON.stringify(model)));
const mr = res.result.results.loadcases["…"].member_results["1"];
// mr.member_displacements: [{ x_frac, displacement: [dx, dy, dz] }, …]
// x_frac 0→1 along the member; displacement in the global input frame.Off by default to keep the payload lean; omitted from member_results when not requested.
Attribution
Free-tier results carry an attribution object minted inside the solver.
Premium results (solved with a valid solve token) do not, so the same code
credits FERS on the free tier and white-labels on Pro:
import { fersAttributionHtml } from "@ferscloud/fers-calculation-web/badge";
// "" when the result came back from a Pro solve.
const credit = fersAttributionHtml(res);getFersAttribution, fersAttributionText and createFersBadge are also
exported. All of them accept the envelope, the parsed model or the results
bundle, and all return nothing for a Pro result or an error envelope.
On 0.2.61 this subpath resolved only under a bundler; plain Node ESM needed
/badge.js. From 0.2.62 the package ships an exports map and both forms work
everywhere.
Using the free tier in a non-commercial application? Please display the credit.
Licence
From 0.2.70 the engine is licensed under the PolyForm Strict License 1.0.0
(see LICENSE). It is free for any noncommercial purpose: study, teaching,
research, hobby projects, and use by schools, charities and public bodies.
Models of more than 100 members need a Pro solve token.
Commercial use, at any model size, needs a paid FERS Cloud plan, which includes the commercial licence. The licence does not allow modifying or redistributing the package.
Links
- Structural analysis in JavaScript — why run the solver client-side
- JavaScript documentation — bundler setup, error codes, types, solve tokens
- Full documentation — every route into FERS
- llms-full.txt — the whole documentation in one fetch, for agents
- FERS Cloud
- FERS Python package —
pip install FERS
