@autochitect/engine 1.1.3 → 1.1.4
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 +65 -186
- package/index.d.ts +1 -1
- package/index.mjs +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,86 +8,13 @@ A 245KB WebAssembly cost estimation engine. Define cost models in a purpose-buil
|
|
|
8
8
|
npm install @autochitect/engine
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Node.js
|
|
12
12
|
|
|
13
13
|
```javascript
|
|
14
14
|
import { createEngine } from '@autochitect/engine';
|
|
15
15
|
|
|
16
|
-
const engine = await createEngine(
|
|
17
|
-
new URL('@autochitect/engine/engine.wasm', import.meta.url)
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
const result = engine.estimate(`
|
|
21
|
-
revenue: Input
|
|
22
|
-
cost_ratio: Input
|
|
23
|
-
costs = revenue * cost_ratio
|
|
24
|
-
profit = revenue - costs
|
|
25
|
-
`, {
|
|
26
|
-
revenue: 500000,
|
|
27
|
-
cost_ratio: 0.6
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
console.log(result.results);
|
|
31
|
-
// { revenue: 500000, cost_ratio: 0.6, costs: 300000, profit: 200000 }
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## What you get
|
|
35
|
-
|
|
36
|
-
**`result.results`** — computed values for every variable.
|
|
37
|
-
|
|
38
|
-
**`result.graph`** — the full dependency DAG showing how each value was derived. Nodes have `kind` (input, formula, map, scan) and edges show data flow. Build audit trails, trace calculations back to source inputs, or render interactive cost breakdowns.
|
|
39
|
-
|
|
40
|
-
**`result.errors`** / **`result.warnings`** — with line and column numbers.
|
|
41
|
-
|
|
42
|
-
## Why use this
|
|
43
|
-
|
|
44
|
-
| Instead of... | You get... |
|
|
45
|
-
|---|---|
|
|
46
|
-
| Building a calculation backend | 245KB client-side engine, zero infrastructure |
|
|
47
|
-
| Exporting Excel/Google Sheets | Version-controlled DSL, real-time interactivity |
|
|
48
|
-
| Spreadsheet-in-browser libraries | Just the engine — no grid UI overhead |
|
|
49
|
-
| Python/R models behind an API | Embeddable in any web app, no server round-trips |
|
|
50
|
-
|
|
51
|
-
The engine compiles your model into a directed acyclic graph, topologically sorts it, and estimates in one pass. Switching scenarios just swaps the input context — sub-millisecond.
|
|
52
|
-
|
|
53
|
-
## The DSL
|
|
54
|
-
|
|
55
|
-
The language is deliberately small. It's designed for cost estimation and financial modeling specifically.
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
# Inputs — bind to external data (your JSON)
|
|
59
|
-
revenue: Input("annual_revenue")
|
|
60
|
-
headcount: Input
|
|
61
|
-
|
|
62
|
-
# Constants — use formula assignment
|
|
63
|
-
tax_rate = 0.21
|
|
64
|
-
avg_salary = 85000
|
|
65
|
-
|
|
66
|
-
# Params — tunable scenario knobs (passed via inputs)
|
|
67
|
-
growth: Param
|
|
68
|
-
|
|
69
|
-
# Formulas — define cost relationships
|
|
70
|
-
labor_cost = headcount * avg_salary
|
|
71
|
-
gross_profit = revenue * (1 - tax_rate)
|
|
72
|
-
net_income = gross_profit * (1 - tax_rate)
|
|
73
|
-
|
|
74
|
-
# Array operations — project over time
|
|
75
|
-
periods = SEQUENCE(12, 1, 1)
|
|
76
|
-
monthly = MAP(periods, LAMBDA(p, revenue / 12 * POWER(1 + growth, p)))
|
|
77
|
-
|
|
78
|
-
# Accumulation — running totals
|
|
79
|
-
cumulative = SCAN(monthly, 0, LAMBDA(acc, m, acc + m))
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
**Inputs** bind to external data (your JSON). **Constants** are defined with formula assignment (`name = value`). **Params** are tunable scenario knobs passed via the inputs object.
|
|
83
|
-
|
|
84
|
-
**MAP** transforms arrays element-wise. **SCAN** accumulates (like reduce, but returns intermediate results). **LAMBDA** defines inline functions with named parameters.
|
|
85
|
-
|
|
86
|
-
## Real-world examples
|
|
16
|
+
const engine = await createEngine();
|
|
87
17
|
|
|
88
|
-
### SaaS pricing calculator
|
|
89
|
-
|
|
90
|
-
```javascript
|
|
91
18
|
const result = engine.estimate(`
|
|
92
19
|
seats: Input
|
|
93
20
|
base_price_per_seat = 12
|
|
@@ -98,7 +25,6 @@ const result = engine.estimate(`
|
|
|
98
25
|
storage_cost = storage_gb * storage_price_per_gb
|
|
99
26
|
subtotal = seat_cost + storage_cost
|
|
100
27
|
|
|
101
|
-
# Volume discount
|
|
102
28
|
discount_rate = IF(seats > 50, 0.20, IF(seats > 20, 0.10, 0))
|
|
103
29
|
discount = subtotal * discount_rate
|
|
104
30
|
monthly_total = subtotal - discount
|
|
@@ -108,130 +34,55 @@ const result = engine.estimate(`
|
|
|
108
34
|
storage_gb: 200,
|
|
109
35
|
});
|
|
110
36
|
|
|
111
|
-
console.log(result.results.monthly_total); // 455
|
|
112
|
-
console.log(result.results.discount_rate); // 0.1 (10% for 35 seats)
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### Construction cost estimation
|
|
116
|
-
|
|
117
|
-
```javascript
|
|
118
|
-
const result = engine.estimate(`
|
|
119
|
-
square_footage: Input
|
|
120
|
-
base_cost_per_sqft: Input
|
|
121
|
-
num_floors: Input
|
|
122
|
-
|
|
123
|
-
material_cost = square_footage * base_cost_per_sqft
|
|
124
|
-
labor_cost = material_cost * 0.60
|
|
125
|
-
floor_premium = (material_cost + labor_cost) * ((num_floors - 1) * 0.08)
|
|
126
|
-
|
|
127
|
-
permits = (material_cost + labor_cost) * 0.03
|
|
128
|
-
insurance = (material_cost + labor_cost) * 0.02
|
|
129
|
-
|
|
130
|
-
total = material_cost + labor_cost + floor_premium + permits + insurance
|
|
131
|
-
cost_per_sqft = total / square_footage
|
|
132
|
-
`, {
|
|
133
|
-
square_footage: 5000,
|
|
134
|
-
base_cost_per_sqft: 150,
|
|
135
|
-
num_floors: 3,
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
console.log(result.results.total); // 1,452,000
|
|
139
|
-
console.log(result.results.cost_per_sqft); // 290.40
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### Loan amortization projection
|
|
143
|
-
|
|
144
|
-
```javascript
|
|
145
|
-
const result = engine.estimate(`
|
|
146
|
-
principal: Input
|
|
147
|
-
annual_rate: Input
|
|
148
|
-
months: Input
|
|
149
|
-
|
|
150
|
-
monthly_rate = annual_rate / 12
|
|
151
|
-
payment = principal * (monthly_rate * POWER(1 + monthly_rate, months))
|
|
152
|
-
/ (POWER(1 + monthly_rate, months) - 1)
|
|
153
|
-
|
|
154
|
-
periods = SEQUENCE(months, 1, 1)
|
|
155
|
-
balances = SCAN(periods, principal, LAMBDA(bal, p,
|
|
156
|
-
bal * (1 + monthly_rate) - payment
|
|
157
|
-
))
|
|
158
|
-
|
|
159
|
-
total_paid = payment * months
|
|
160
|
-
total_interest = total_paid - principal
|
|
161
|
-
`, {
|
|
162
|
-
principal: 400000,
|
|
163
|
-
annual_rate: 0.065,
|
|
164
|
-
months: 360,
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
console.log(result.results.payment); // ~2,528.27/month
|
|
168
|
-
console.log(result.results.total_interest); // ~510,177
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
## Node.js usage
|
|
172
|
-
|
|
173
|
-
```javascript
|
|
174
|
-
import { createEngine } from '@autochitect/engine';
|
|
175
|
-
import { createRequire } from 'module';
|
|
176
|
-
|
|
177
|
-
const require = createRequire(import.meta.url);
|
|
178
|
-
const wasmPath = require.resolve('@autochitect/engine/engine.wasm');
|
|
179
|
-
const engine = await createEngine(wasmPath);
|
|
180
|
-
|
|
181
|
-
const result = engine.estimate(`
|
|
182
|
-
headcount: Input
|
|
183
|
-
avg_salary = 85000
|
|
184
|
-
benefits_rate = 0.3
|
|
185
|
-
|
|
186
|
-
labor_cost = headcount * avg_salary
|
|
187
|
-
benefits = labor_cost * benefits_rate
|
|
188
|
-
total_people_cost = labor_cost + benefits
|
|
189
|
-
`, {
|
|
190
|
-
headcount: 12,
|
|
191
|
-
});
|
|
192
|
-
|
|
193
37
|
console.log(result.results);
|
|
194
38
|
// {
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
// labor_cost: 1020000,
|
|
199
|
-
// benefits: 306000,
|
|
200
|
-
// total_people_cost: 1326000
|
|
39
|
+
// seats: 35, base_price_per_seat: 12, storage_gb: 200, storage_price_per_gb: 0.5,
|
|
40
|
+
// seat_cost: 420, storage_cost: 100, subtotal: 520,
|
|
41
|
+
// discount_rate: 0.1, discount: 52, monthly_total: 468, annual_total: 5616
|
|
201
42
|
// }
|
|
202
43
|
```
|
|
203
44
|
|
|
204
|
-
## Browser
|
|
45
|
+
## Browser
|
|
46
|
+
|
|
47
|
+
No install needed — paste this into an HTML file and open it.
|
|
205
48
|
|
|
206
49
|
```html
|
|
207
50
|
<script type="module">
|
|
208
|
-
import { createEngine } from '
|
|
51
|
+
import { createEngine } from 'https://esm.sh/@autochitect/engine';
|
|
209
52
|
|
|
210
53
|
const engine = await createEngine(
|
|
211
|
-
|
|
54
|
+
'https://unpkg.com/@autochitect/engine/engine.wasm'
|
|
212
55
|
);
|
|
213
56
|
|
|
214
57
|
const result = engine.estimate(`
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
58
|
+
seats: Input
|
|
59
|
+
base_price_per_seat = 12
|
|
60
|
+
storage_gb: Input
|
|
61
|
+
storage_price_per_gb = 0.50
|
|
62
|
+
|
|
63
|
+
seat_cost = seats * base_price_per_seat
|
|
64
|
+
storage_cost = storage_gb * storage_price_per_gb
|
|
65
|
+
subtotal = seat_cost + storage_cost
|
|
218
66
|
|
|
219
|
-
|
|
67
|
+
discount_rate = IF(seats > 50, 0.20, IF(seats > 20, 0.10, 0))
|
|
220
68
|
discount = subtotal * discount_rate
|
|
221
|
-
|
|
69
|
+
monthly_total = subtotal - discount
|
|
70
|
+
annual_total = monthly_total * 12
|
|
222
71
|
`, {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
discount_rate: 0.15,
|
|
72
|
+
seats: 35,
|
|
73
|
+
storage_gb: 200,
|
|
226
74
|
});
|
|
227
75
|
|
|
228
76
|
console.log(result.results);
|
|
229
|
-
// {
|
|
230
|
-
//
|
|
77
|
+
// {
|
|
78
|
+
// seats: 35, base_price_per_seat: 12, storage_gb: 200, storage_price_per_gb: 0.5,
|
|
79
|
+
// seat_cost: 420, storage_cost: 100, subtotal: 520,
|
|
80
|
+
// discount_rate: 0.1, discount: 52, monthly_total: 468, annual_total: 5616
|
|
81
|
+
// }
|
|
231
82
|
</script>
|
|
232
83
|
```
|
|
233
84
|
|
|
234
|
-
|
|
85
|
+
If you use a bundler (Vite, webpack, etc.), import normally:
|
|
235
86
|
|
|
236
87
|
```javascript
|
|
237
88
|
import { createEngine } from '@autochitect/engine';
|
|
@@ -241,16 +92,44 @@ const engine = await createEngine(
|
|
|
241
92
|
);
|
|
242
93
|
```
|
|
243
94
|
|
|
244
|
-
##
|
|
95
|
+
## What you get
|
|
96
|
+
|
|
97
|
+
**`result.results`** — computed values for every variable.
|
|
98
|
+
|
|
99
|
+
**`result.graph`** — the full dependency DAG showing how each value was derived. Nodes have `kind` (input, formula, map, scan) and edges show data flow. Build audit trails, trace calculations back to source inputs, or render interactive cost breakdowns.
|
|
100
|
+
|
|
101
|
+
**`result.errors`** / **`result.warnings`** — with line and column numbers.
|
|
102
|
+
|
|
103
|
+
## The DSL
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
# Inputs — bind to external data (your JSON)
|
|
107
|
+
revenue: Input("annual_revenue")
|
|
108
|
+
headcount: Input
|
|
109
|
+
|
|
110
|
+
# Constants — use formula assignment
|
|
111
|
+
tax_rate = 0.21
|
|
112
|
+
avg_salary = 85000
|
|
113
|
+
|
|
114
|
+
# Params — tunable scenario knobs (passed via inputs)
|
|
115
|
+
growth: Param
|
|
116
|
+
|
|
117
|
+
# Formulas — define cost relationships
|
|
118
|
+
labor_cost = headcount * avg_salary
|
|
119
|
+
gross_profit = revenue * (1 - tax_rate)
|
|
120
|
+
net_income = gross_profit * (1 - tax_rate)
|
|
121
|
+
|
|
122
|
+
# Array operations — project over time
|
|
123
|
+
periods = SEQUENCE(12, 1, 1)
|
|
124
|
+
monthly = MAP(periods, LAMBDA(p, revenue / 12 * POWER(1 + growth, p)))
|
|
125
|
+
|
|
126
|
+
# Accumulation — running totals
|
|
127
|
+
cumulative = SCAN(monthly, 0, LAMBDA(acc, m, acc + m))
|
|
128
|
+
```
|
|
245
129
|
|
|
246
|
-
|
|
130
|
+
**Inputs** bind to external data (your JSON). **Constants** are defined with formula assignment (`name = value`). **Params** are tunable scenario knobs passed via the inputs object.
|
|
247
131
|
|
|
248
|
-
|
|
249
|
-
|---------|-------------|
|
|
250
|
-
| [Next.js pricing calculator](https://github.com/autochitect/engine/tree/main/examples/nextjs-pricing-calculator) | SaaS pricing page with real-time sliders and volume discounts |
|
|
251
|
-
| [Vue.js cost estimator](https://github.com/autochitect/engine/tree/main/examples/vue-cost-estimator) | Construction cost estimator with dependency graph visualization |
|
|
252
|
-
| [Strapi pricing page](https://github.com/autochitect/engine/tree/main/examples/strapi-pricing-page) | CMS-driven pricing — content in Strapi, calculations in engine |
|
|
253
|
-
| [Vanilla JS](https://github.com/autochitect/engine/tree/main/examples/vanilla-js) | Single HTML file, no build tools, plain DOM |
|
|
132
|
+
**MAP** transforms arrays element-wise. **SCAN** accumulates (like reduce, but returns intermediate results). **LAMBDA** defines inline functions with named parameters.
|
|
254
133
|
|
|
255
134
|
## Options
|
|
256
135
|
|
package/index.d.ts
CHANGED
|
@@ -24,4 +24,4 @@ export interface Engine {
|
|
|
24
24
|
estimate(source: string, inputs?: Record<string, unknown> | object, options?: EstimateOptions): EstimateResult;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export function createEngine(wasmSource
|
|
27
|
+
export function createEngine(wasmSource?: string | URL | Response | ArrayBuffer): Promise<Engine>;
|
package/index.mjs
CHANGED
|
@@ -82,6 +82,13 @@ export async function createEngine(wasmSource) {
|
|
|
82
82
|
const memoryRef = { current: null };
|
|
83
83
|
const imports = { wasi_snapshot_preview1: buildWasiImports(memoryRef) };
|
|
84
84
|
|
|
85
|
+
if (wasmSource == null && typeof globalThis.process !== 'undefined') {
|
|
86
|
+
const { fileURLToPath } = await import('url');
|
|
87
|
+
const { dirname, join } = await import('path');
|
|
88
|
+
const fs = await import('fs');
|
|
89
|
+
wasmSource = join(dirname(fileURLToPath(import.meta.url)), 'engine.wasm');
|
|
90
|
+
}
|
|
91
|
+
|
|
85
92
|
let bytes;
|
|
86
93
|
if (wasmSource instanceof ArrayBuffer || ArrayBuffer.isView(wasmSource)) {
|
|
87
94
|
bytes = wasmSource;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autochitect/engine",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "A 245KB WebAssembly financial modeling engine. Define cost models in a purpose-built DSL, get instant estimates with full dependency tracing. No server required.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|