@ferscloud/fers-calculation-web 0.2.39 → 0.2.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,25 +2,82 @@
2
2
 
3
3
  High-performance structural engineering FEM solver compiled from Rust to WebAssembly. Runs entirely in the browser — no server round-trip per calculation.
4
4
 
5
+ > 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.
6
+
5
7
  ## Installation
6
8
 
7
9
  ```bash
10
+ # Browser apps (Vite, Next.js, webpack):
11
+ npm install @ferscloud/fers-calculation-web
12
+ # Node.js / server-side:
8
13
  npm install @ferscloud/fers-calculation
9
14
  ```
10
15
 
11
- ## Quick start — free tier (up to 100 members)
16
+ ## Bundler setup (browser build)
17
+
18
+ `@ferscloud/fers-calculation-web` is a WASM ES module that initialises via top-level `await`. Most bundlers need a one-time config:
19
+
20
+ **Vite**
21
+
22
+ ```bash
23
+ npm i -D vite-plugin-wasm vite-plugin-top-level-await
24
+ ```
12
25
 
13
26
  ```ts
14
- import init, { calculate_from_json } from "@ferscloud/fers-calculation";
27
+ // vite.config.ts
28
+ import wasm from "vite-plugin-wasm";
29
+ import topLevelAwait from "vite-plugin-top-level-await";
15
30
 
16
- await init(); // load the WASM module once
31
+ export default { plugins: [wasm(), topLevelAwait()] };
32
+ ```
17
33
 
18
- const result = calculate_from_json(JSON.stringify(myModel));
19
- const data = JSON.parse(result);
34
+ **Next.js / webpack**
35
+
36
+ ```js
37
+ // next.config.js
38
+ module.exports = {
39
+ transpilePackages: ["@ferscloud/fers-calculation-web"],
40
+ webpack(config) {
41
+ config.experiments = { ...config.experiments, asyncWebAssembly: true };
42
+ config.output.environment = { ...config.output.environment, asyncFunction: true };
43
+ return config;
44
+ },
45
+ };
46
+ ```
47
+
48
+ 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.
49
+
50
+ ## Quick start — free tier (up to 100 members)
51
+
52
+ ```ts
53
+ import { calculate_from_json } from "@ferscloud/fers-calculation";
54
+
55
+ // No init() call needed — the nodejs and bundler builds initialise the
56
+ // WASM module automatically on import.
57
+ const res = JSON.parse(calculate_from_json(JSON.stringify(myModel)));
58
+ if (res.ok) {
59
+ const data = res.result; // displacements, member_results, unity_checks, …
60
+ } else {
61
+ console.error(res.error.code, res.error.message);
62
+ }
20
63
  ```
21
64
 
22
65
  No API key required. Works for any model with up to 100 members.
23
66
 
67
+ ## Response shape
68
+
69
+ 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:
70
+
71
+ ```jsonc
72
+ // success
73
+ { "ok": true, "result": { /* the full result document */ } }
74
+ // failure (invalid model, over the member limit, malformed JSON, …)
75
+ { "ok": false, "error": { "code": "LimitExceeded",
76
+ "message": "Number of members (250) exceeds allowed maximum of 100" } }
77
+ ```
78
+
79
+ `error.code` is one of `InvalidJson`, `LimitExceeded`, `SolveError`, `InternalPanic`, `InternalSerialization`. The return value is **always** valid JSON.
80
+
24
81
  ## Authenticated tier — Pro (up to 10 000 members)
25
82
 
26
83
  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.
@@ -74,17 +131,16 @@ Same pattern works for Express, Deno, Bun, Cloudflare Workers, or any server-sid
74
131
  ### 3. Frontend: call the solver with the token
75
132
 
76
133
  ```ts
77
- import init, { calculate_from_json_with_token } from "@ferscloud/fers-calculation";
78
-
79
- await init();
134
+ import { calculate_from_json_with_token } from "@ferscloud/fers-calculation";
80
135
 
81
136
  const { token } = await fetch("/api/solve-token").then(r => r.json());
82
137
 
83
- const result = calculate_from_json_with_token(JSON.stringify(myModel), token);
84
- const data = JSON.parse(result);
138
+ const res = JSON.parse(calculate_from_json_with_token(JSON.stringify(myModel), token));
139
+ if (!res.ok) throw new Error(`${res.error.code}: ${res.error.message}`);
140
+ const data = res.result;
85
141
  ```
86
142
 
87
- If the token is missing, expired, or invalid, the solver falls back to the free 100-member limit — it never throws.
143
+ 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).
88
144
 
89
145
  ## Tier comparison
90
146
 
@@ -99,6 +155,36 @@ If the token is missing, expired, or invalid, the solver falls back to the free
99
155
 
100
156
  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.
101
157
 
158
+ ## TypeScript types
159
+
160
+ The browser package ships generated model types alongside the function signatures:
161
+
162
+ ```ts
163
+ import type { FERS, ResultsBundle } from "@ferscloud/fers-calculation-web/fers-models";
164
+ ```
165
+
166
+ `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.
167
+
168
+ ## Deflected shape (optional)
169
+
170
+ Set `include_member_deflected_shape` in the model's analysis options to get a
171
+ ready-to-plot, **load-exact** deflected shape per member — the member's global
172
+ displacement sampled along its length — instead of reconstructing the curve yourself:
173
+
174
+ ```ts
175
+ const model = {
176
+ /* … model + load cases … */
177
+ analysis: { /* … */ options: { /* … */ include_member_deflected_shape: true } },
178
+ };
179
+
180
+ const res = JSON.parse(calculate_from_json(JSON.stringify(model)));
181
+ const mr = res.result.results.loadcases["…"].member_results["1"];
182
+ // mr.member_displacements: [{ x_frac, displacement: [dx, dy, dz] }, …]
183
+ // x_frac 0→1 along the member; displacement in the global input frame.
184
+ ```
185
+
186
+ Off by default to keep the payload lean; omitted from `member_results` when not requested.
187
+
102
188
  ## Links
103
189
 
104
190
  - [Full documentation & getting started](https://ferscloud.com/getting-started)