Technical Report: Fixing the Bazi (八字) Chart Calculation Feature in Weishu (緯書)
Technical report on fixing a WASM loading failure in the Bazi (八字) chart tool, covering Cloudflare CDN compression conflict investigation and an edge-computing decompression solution.
1. Problem Background
After refactoring the Bazi (八字, Eight Characters) chart tool (WASM version) on the Weishu (緯書) blog to the unified Boshu (帛書) visual style and deploying it to Cloudflare Workers, users reported that clicking “Calculate Chart” (推算命盤) on the front-end page produced an error: Calculation failed: unable to load WASM file. Meanwhile, the console printed a magic-number mismatch error: expected magic word 00 61 73 6d, found cf ff ff 7f.
This issue completely broke the core Bazi chart-drawing functionality. This report documents in detail the troubleshooting process, root-cause analysis, and the final fix.
2. Diagnosis and Root-Cause Analysis
Through in-depth investigation of the front-end loading logic, network request capture, and the WebAssembly instantiation process, we found that this failure was not caused by a single issue, but by the combination of three independent technical problems.
2.1 Compression Format Conflict and CDN Interception (Core Issue)
Symptoms:
The front-end attempted to fetch ganzhi.wasm.gz (18MB) via fetch and decompress it using DecompressionStream('gzip'). However, the first 8 bytes of the byte stream passed to WebAssembly.instantiate after decompression were cf ff ff 7f, not the standard WASM magic number 00 61 73 6d.
Analysis:
cf ff ff 7f is the file header signature of the Brotli compression algorithm. Further packet capture revealed that although the front-end requested the .gz file, the browser automatically sent an Accept-Encoding: gzip, deflate, br header with the request. The Cloudflare CDN edge node detected that the client supported Brotli and proactively re-compressed the original Gzip file with Brotli, returning Content-Encoding: br.
As a result, the front-end DecompressionStream('gzip') received Brotli-format data, decompression failed, and the WebAssembly engine threw a fatal magic-number mismatch error.
2.2 Abnormal JSON Data Parsing Path
Symptoms: After resolving the WASM loading issue, the front-end console showed that calculation had completed, but the Four Pillars (四柱), Five Elements (五行), and Da Yun (大運, fortune cycles) UI elements still displayed the placeholder ”—”.
Analysis:
The original JS glue code accessed top-level properties such as data.pillars directly when processing the JSON string returned by WASM. However, the data structure returned by the newly compiled WASM module was wrapped in a result object, with the actual structure being {"result": {"pillars": {...}}, "success": true}. Because the path was incorrect, the front-end rendering function received undefined, and the UI could not update.
2.3 Field Format Incompatibility
Symptoms: The Heavenly Stem and Earthly Branch (天干地支) data for the Four Pillars failed to populate the corresponding DOM nodes correctly.
Analysis:
The legacy rendering logic expected WASM to return separate stem and branch fields (e.g., yearStem: "庚", yearBranch: "午"). But the actual returned data combined the stem and branch into a single string (e.g., year: "庚午"). This caused the front-end destructuring assignment to fail.
3. Fix Implementation
To address the three issues above, we adopted a combined strategy of “server-side decompression + front-end adaptation.”
3.1 Edge Computing Layer (Cloudflare Workers) Refactor
To completely avoid the automatic compression content negotiation conflict between the browser and the CDN, we abandoned the approach of decompressing on the front-end with DecompressionStream and instead leveraged the edge-computing capabilities of Cloudflare Workers.
Implementation Details:
We wrote an interception script, worker.js, that detects requests for /dist/ganzhi.wasm. When such a request arrives, the Worker reads the ganzhi.wasm.gz static asset on the server side, completes Gzip decompression at the edge node, forcibly sets Content-Type: application/wasm, and returns the clean, raw WASM byte stream directly to the front-end.
This approach not only resolved the double-compression conflict but also utilized edge-node compute power to relieve the decompression burden on client browsers.
3.2 Front-End Rendering Logic Fix
To accommodate the changes in JSON structure and field formats, we rewrote the renderResult function in index.html.
| Fix Item | Old Logic | New Logic |
|---|---|---|
| Data Hierarchy | const p = data.pillars; | const data = raw.result ? raw.result : raw; const p = data.pillars; |
| Pillar Splitting | [p.yearStem, p.yearBranch] | const sb = p.year; $(sId).textContent = sb[0]; $(bId).textContent = sb.slice(1); |
| Five Elements Rendering | Relied on specific Chinese character key values | Added a Chinese-to-English dictionary mapping, supporting dynamic generation of HTML tags with the appropriate CSS color classes |
3.3 Visual Style Unification
While fixing the functionality, we also deeply unified the Bazi chart’s UI styling with the main Weishu (緯書) blog site. This included introducing a paper-white background (#FAF7F2), cinnabar red accent color (#C0392B), and ink black text (#1A1A1A), along with a consistent top navigation bar for returning to the main site, ensuring overall brand visual coherence.
4. Summary
This fix successfully resolved the conflict between Cloudflare CDN’s automatic compression mechanism and WebAssembly loading. By moving the decompression logic to the Workers edge node and refactoring the front-end data parsing path, the Bazi chart tool (八字命盤工具) is now back to stable operation.
This case also provides valuable experience for deploying large WASM files in Serverless architectures in the future: when handling non-standard binary assets, be wary of the CDN’s default transparent compression behavior; when necessary, use custom Worker scripts to explicitly intervene.