hledger-wasm 0.1.0
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 +35 -0
- package/dist/hledger-wasm.wasm +0 -0
- package/js/hledger-bridge.js +180 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# hledger-wasm
|
|
2
|
+
|
|
3
|
+
hledger compiled to WebAssembly for running in the browser.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
import { init, accounts, balance, print, aregister, commodities } from 'hledger-wasm';
|
|
9
|
+
|
|
10
|
+
await init();
|
|
11
|
+
|
|
12
|
+
const journal = `
|
|
13
|
+
2024-01-01 Opening
|
|
14
|
+
assets:bank 1000 USD
|
|
15
|
+
equity:opening
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const accountList = await accounts(journal);
|
|
19
|
+
const balances = await balance(journal);
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## API
|
|
23
|
+
|
|
24
|
+
All functions take journal content as a string and return Promises.
|
|
25
|
+
|
|
26
|
+
- `init()` - Initialize the WASM module (call once)
|
|
27
|
+
- `accounts(journal)` - Get account names as `string[]`
|
|
28
|
+
- `print(journal)` - Get transactions as objects
|
|
29
|
+
- `balance(journal)` - Get balances as `[accountName, amounts][]`
|
|
30
|
+
- `aregister(journal, account)` - Get transactions for an account
|
|
31
|
+
- `commodities(journal)` - Get commodity names as `string[]`
|
|
32
|
+
|
|
33
|
+
## window.hledgerWasm
|
|
34
|
+
|
|
35
|
+
The bridge automatically exposes these functions on `window.hledgerWasm`.
|
|
Binary file
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hledger-wasm JavaScript bridge
|
|
3
|
+
* Provides window.hledgerWasm API for running hledger commands in the browser
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { WASI, OpenFile, File, ConsoleStdout, PreopenDirectory } from "@bjorn3/browser_wasi_shim";
|
|
7
|
+
|
|
8
|
+
let wasmInstance = null;
|
|
9
|
+
let wasiInstance = null;
|
|
10
|
+
|
|
11
|
+
// Configurable WASM path - can be set before calling init()
|
|
12
|
+
let wasmPath = "/wasm/hledger-wasm.wasm";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Set the path to the hledger WASM file
|
|
16
|
+
*/
|
|
17
|
+
export function setWasmPath(path) {
|
|
18
|
+
wasmPath = path;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Initialize the hledger WASM module
|
|
23
|
+
* Must be called once before using other functions
|
|
24
|
+
*/
|
|
25
|
+
export async function init() {
|
|
26
|
+
if (wasmInstance) {
|
|
27
|
+
return; // Already initialized
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const response = await fetch(wasmPath);
|
|
31
|
+
const wasmBytes = await response.arrayBuffer();
|
|
32
|
+
|
|
33
|
+
// Create a minimal WASI environment
|
|
34
|
+
const fds = [
|
|
35
|
+
new OpenFile(new File([])), // stdin
|
|
36
|
+
ConsoleStdout.lineBuffered((msg) => console.log("[hledger]", msg)),
|
|
37
|
+
ConsoleStdout.lineBuffered((msg) => console.error("[hledger]", msg)),
|
|
38
|
+
new PreopenDirectory("/", new Map()),
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
wasiInstance = new WASI([], [], fds, { debug: false });
|
|
42
|
+
|
|
43
|
+
const { instance } = await WebAssembly.instantiate(wasmBytes, {
|
|
44
|
+
wasi_snapshot_preview1: wasiInstance.wasiImport,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
wasmInstance = instance;
|
|
48
|
+
wasiInstance.initialize(instance);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Write journal content to virtual filesystem and run hledger command
|
|
53
|
+
*/
|
|
54
|
+
async function runHledger(journalContent, command, ...args) {
|
|
55
|
+
if (!wasmInstance) {
|
|
56
|
+
throw new Error("hledger WASM not initialized. Call init() first.");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Capture stdout
|
|
60
|
+
let stdout = "";
|
|
61
|
+
let stderr = "";
|
|
62
|
+
|
|
63
|
+
const fds = [
|
|
64
|
+
new OpenFile(new File([])), // stdin
|
|
65
|
+
ConsoleStdout.lineBuffered((msg) => { stdout += msg + "\n"; }),
|
|
66
|
+
ConsoleStdout.lineBuffered((msg) => { stderr += msg + "\n"; }),
|
|
67
|
+
new PreopenDirectory("/", new Map([
|
|
68
|
+
["journal.hledger", new File(new TextEncoder().encode(journalContent))],
|
|
69
|
+
])),
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const wasi = new WASI(
|
|
73
|
+
["hledger-wasm", command, "/journal.hledger", ...args],
|
|
74
|
+
[],
|
|
75
|
+
fds,
|
|
76
|
+
{ debug: false }
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const response = await fetch(wasmPath);
|
|
80
|
+
const wasmBytes = await response.arrayBuffer();
|
|
81
|
+
|
|
82
|
+
const { instance } = await WebAssembly.instantiate(wasmBytes, {
|
|
83
|
+
wasi_snapshot_preview1: wasi.wasiImport,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
wasi.initialize(instance);
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
instance.exports._start();
|
|
90
|
+
} catch (e) {
|
|
91
|
+
if (e.message !== "exit with exit code 0" && !e.message.includes("unreachable")) {
|
|
92
|
+
console.error("hledger error:", e);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (stderr.trim()) {
|
|
97
|
+
console.warn("hledger stderr:", stderr);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return stdout.trim();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get account names from journal
|
|
105
|
+
* @param {string} journal - Journal content as string
|
|
106
|
+
* @returns {Promise<string[]>} - Array of account names
|
|
107
|
+
*/
|
|
108
|
+
export async function accounts(journal) {
|
|
109
|
+
const result = await runHledger(journal, "accounts");
|
|
110
|
+
if (!result) return [];
|
|
111
|
+
return JSON.parse(result);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get transactions (print command)
|
|
116
|
+
* @param {string} journal - Journal content as string
|
|
117
|
+
* @returns {Promise<object[]>} - Array of transaction objects
|
|
118
|
+
*/
|
|
119
|
+
export async function print(journal) {
|
|
120
|
+
const result = await runHledger(journal, "print");
|
|
121
|
+
if (!result) return [];
|
|
122
|
+
return JSON.parse(result);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get account balances
|
|
127
|
+
* @param {string} journal - Journal content as string
|
|
128
|
+
* @returns {Promise<Array<[string, object[]]>>} - Array of [accountName, amounts] tuples
|
|
129
|
+
*/
|
|
130
|
+
export async function balance(journal) {
|
|
131
|
+
const result = await runHledger(journal, "balance");
|
|
132
|
+
if (!result) return [];
|
|
133
|
+
return JSON.parse(result);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get account register (transactions for a specific account)
|
|
138
|
+
* @param {string} journal - Journal content as string
|
|
139
|
+
* @param {string} account - Account name or prefix
|
|
140
|
+
* @returns {Promise<object[]>} - Array of transaction objects
|
|
141
|
+
*/
|
|
142
|
+
export async function aregister(journal, account) {
|
|
143
|
+
const result = await runHledger(journal, "aregister", account);
|
|
144
|
+
if (!result) return [];
|
|
145
|
+
return JSON.parse(result);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get commodities used in journal
|
|
150
|
+
* @param {string} journal - Journal content as string
|
|
151
|
+
* @returns {Promise<string[]>} - Array of commodity names
|
|
152
|
+
*/
|
|
153
|
+
export async function commodities(journal) {
|
|
154
|
+
const result = await runHledger(journal, "commodities");
|
|
155
|
+
if (!result) return [];
|
|
156
|
+
return JSON.parse(result);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Expose on window for Rust wasm_bindgen interop
|
|
160
|
+
if (typeof window !== "undefined") {
|
|
161
|
+
window.hledgerWasm = {
|
|
162
|
+
init,
|
|
163
|
+
setWasmPath,
|
|
164
|
+
accounts,
|
|
165
|
+
print,
|
|
166
|
+
balance,
|
|
167
|
+
aregister,
|
|
168
|
+
commodities,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export default {
|
|
173
|
+
init,
|
|
174
|
+
setWasmPath,
|
|
175
|
+
accounts,
|
|
176
|
+
print,
|
|
177
|
+
balance,
|
|
178
|
+
aregister,
|
|
179
|
+
commodities,
|
|
180
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hledger-wasm",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "hledger compiled to WebAssembly for browser use",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "js/hledger-bridge.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./js/hledger-bridge.js",
|
|
9
|
+
"./hledger-wasm.wasm": "./dist/hledger-wasm.wasm"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"js/hledger-bridge.js",
|
|
13
|
+
"dist/hledger-wasm.wasm"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "./build.sh"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@bjorn3/browser_wasi_shim": "^0.4.2"
|
|
20
|
+
},
|
|
21
|
+
"keywords": ["hledger", "wasm", "webassembly", "accounting", "ledger"],
|
|
22
|
+
"license": "GPL-3.0-or-later",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/reesericci/hledger-wasm"
|
|
26
|
+
}
|
|
27
|
+
}
|