@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 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
- ## Quick start
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
- // headcount: 12,
196
- // avg_salary: 85000,
197
- // benefits_rate: 0.3,
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 usage
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 './node_modules/@autochitect/engine/index.mjs';
51
+ import { createEngine } from 'https://esm.sh/@autochitect/engine';
209
52
 
210
53
  const engine = await createEngine(
211
- new URL('./node_modules/@autochitect/engine/engine.wasm', import.meta.url)
54
+ 'https://unpkg.com/@autochitect/engine/engine.wasm'
212
55
  );
213
56
 
214
57
  const result = engine.estimate(`
215
- units: Input
216
- price_per_unit: Input
217
- discount_rate: Input
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
- subtotal = units * price_per_unit
67
+ discount_rate = IF(seats > 50, 0.20, IF(seats > 20, 0.10, 0))
220
68
  discount = subtotal * discount_rate
221
- total = subtotal - discount
69
+ monthly_total = subtotal - discount
70
+ annual_total = monthly_total * 12
222
71
  `, {
223
- units: 1000,
224
- price_per_unit: 49.99,
225
- discount_rate: 0.15,
72
+ seats: 35,
73
+ storage_gb: 200,
226
74
  });
227
75
 
228
76
  console.log(result.results);
229
- // { units: 1000, price_per_unit: 49.99, discount_rate: 0.15,
230
- // subtotal: 49990, discount: 7498.5, total: 42491.5 }
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
- When using a bundler (Vite, webpack, etc.), import the WASM file directly:
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
- ## Framework examples
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
- Working integration examples are available in the [`examples/`](https://github.com/autochitect/engine/tree/main/examples) directory:
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
- | Example | Description |
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: string | URL | Response | ArrayBuffer): Promise<Engine>;
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",
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",