@mog-sdk/node 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 +277 -0
- package/dist/index.cjs +86096 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +377 -0
- package/dist/index.d.ts +377 -0
- package/dist/index.js +86064 -0
- package/dist/index.js.map +1 -0
- package/package.json +76 -0
package/README.md
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# @mog/sdk
|
|
2
|
+
|
|
3
|
+
Shortcut Data OS SDK — headless spreadsheet engine for Node.js. Runs the real kernel + Rust compute-core without a browser.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { createWorkbook } from '@mog/sdk';
|
|
9
|
+
|
|
10
|
+
const wb = await createWorkbook();
|
|
11
|
+
const ws = wb.activeSheet;
|
|
12
|
+
|
|
13
|
+
await ws.setCell('A1', 42);
|
|
14
|
+
await ws.setCell('A2', 58);
|
|
15
|
+
await ws.setCell('A3', '=A1+A2');
|
|
16
|
+
|
|
17
|
+
const val = await ws.getValue('A3'); // 100
|
|
18
|
+
await wb.dispose();
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Three lines to a running spreadsheet engine. No addon injection, no context threading, no active-sheet callbacks.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Monorepo — already available as workspace dependency
|
|
27
|
+
pnpm add @mog/sdk
|
|
28
|
+
|
|
29
|
+
# The native Rust addon must be built first
|
|
30
|
+
cd compute-core-napi && pnpm build
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## API Reference
|
|
34
|
+
|
|
35
|
+
### Creating Workbooks
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// Zero-arg: blank workbook, auto-detects platform
|
|
39
|
+
const wb = await createWorkbook();
|
|
40
|
+
|
|
41
|
+
// With options: import XLSX
|
|
42
|
+
const wb = await createWorkbook({
|
|
43
|
+
source: { type: 'bytes', data: new Uint8Array(buffer) }
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Power-user: pre-existing kernel context (browser app path)
|
|
47
|
+
const wb = await createWorkbook({ ctx, eventBus });
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Reading Data
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
const ws = wb.activeSheet;
|
|
54
|
+
|
|
55
|
+
// Single cell value (computed)
|
|
56
|
+
const val = await ws.getValue('A1'); // CellValue | null
|
|
57
|
+
const val2 = await ws.getValue(0, 0); // numeric addressing
|
|
58
|
+
|
|
59
|
+
// Full cell data (value + formula + format)
|
|
60
|
+
const cell = await ws.getCell('A1');
|
|
61
|
+
|
|
62
|
+
// All data in used range as 2D array
|
|
63
|
+
const data = await ws.getData(); // CellValue[][]
|
|
64
|
+
|
|
65
|
+
// Range read
|
|
66
|
+
const range = await ws.getRange('A1:C10'); // CellData[][]
|
|
67
|
+
|
|
68
|
+
// LLM-friendly presentation
|
|
69
|
+
await ws.describe('A3'); // "100(=A1+A2)"
|
|
70
|
+
await ws.describeRange('A1:B3'); // Tabular text
|
|
71
|
+
await ws.summarize(); // Full sheet overview
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Writing Data
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// Single cell (A1 or numeric)
|
|
78
|
+
await ws.setCell('A1', 42);
|
|
79
|
+
await ws.setCell('A2', '=A1*2');
|
|
80
|
+
await ws.setCell(2, 0, 'text');
|
|
81
|
+
|
|
82
|
+
// Bulk write
|
|
83
|
+
await ws.setRange('A1', [
|
|
84
|
+
['Name', 'Score'],
|
|
85
|
+
['Alice', 92],
|
|
86
|
+
['Bob', 85],
|
|
87
|
+
]);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Working with Sheets
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
const count = await wb.getSheetCount();
|
|
94
|
+
const names = await wb.getSheetNames();
|
|
95
|
+
const ws2 = await wb.sheets.add('Sheet2');
|
|
96
|
+
|
|
97
|
+
// Get or create (idempotent)
|
|
98
|
+
const { sheet, created } = await wb.getOrCreateSheet('Data');
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Tables and Structured Data
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// One-liner table creation
|
|
105
|
+
await ws.createTable('SalesData', {
|
|
106
|
+
headers: ['Product', 'Q1', 'Q2'],
|
|
107
|
+
data: [
|
|
108
|
+
['Widget', 100, 150],
|
|
109
|
+
['Gadget', 200, 180],
|
|
110
|
+
],
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Serialization
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// CSV (RFC 4180, formula injection protected)
|
|
118
|
+
const csv = await ws.toCSV();
|
|
119
|
+
const tsvData = await ws.toCSV({ separator: '\t' });
|
|
120
|
+
|
|
121
|
+
// JSON (array of objects, first row as headers)
|
|
122
|
+
const json = await ws.toJSON();
|
|
123
|
+
// [{ Product: 'Widget', Q1: 100, Q2: 150 }, ...]
|
|
124
|
+
|
|
125
|
+
const jsonNoHeader = await ws.toJSON({ headerRow: 'none' });
|
|
126
|
+
// [{ A: 'Product', B: 'Q1', C: 'Q2' }, ...]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### File I/O (SDK-only, Node.js)
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { save } from '@mog/sdk';
|
|
133
|
+
|
|
134
|
+
// Export workbook to file (format by extension)
|
|
135
|
+
await save(wb, 'output.xlsx');
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Formulas
|
|
139
|
+
|
|
140
|
+
Each `setCell` mutation triggers automatic recalc in Rust. Formulas are evaluated by the time `setCell` returns — no manual `calculate()` needed.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
await ws.setCell('A1', 10);
|
|
144
|
+
await ws.setCell('A2', 20);
|
|
145
|
+
await ws.setCell('A3', '=SUM(A1:A2)');
|
|
146
|
+
|
|
147
|
+
const val = await ws.getValue('A3'); // 30
|
|
148
|
+
|
|
149
|
+
// Search by formula
|
|
150
|
+
const cells = await ws.findByFormula(/SUM/); // ['A3']
|
|
151
|
+
const formula = await ws.getFormula('A3'); // '=SUM(A1:A2)'
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Formatting & Structure
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
await ws.setFormat('A1', { bold: true, fontColor: '#FF0000' });
|
|
158
|
+
await ws.setRangeFormat('A1:B3', { italic: true });
|
|
159
|
+
|
|
160
|
+
await ws.structure.insertRows(2, 3);
|
|
161
|
+
await ws.structure.deleteColumns(1, 1);
|
|
162
|
+
await ws.structure.mergeCells('A1:B1');
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Full Kernel API
|
|
166
|
+
|
|
167
|
+
The full kernel API is accessible — charts, tables, filters, validation, conditional formatting, pivots, and all 23 domain sub-APIs:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// Charts
|
|
171
|
+
await ws.charts.add({ type: 'bar', dataRange: 'A1:B5' });
|
|
172
|
+
|
|
173
|
+
// Conditional formatting
|
|
174
|
+
await ws.conditionalFormatting.add({ range: 'B2:B10', rule: { type: 'greaterThan', value: 100 } });
|
|
175
|
+
|
|
176
|
+
// Tables
|
|
177
|
+
await ws.tables.add('MyTable', 'A1:C5', { hasHeaders: true });
|
|
178
|
+
|
|
179
|
+
// Filters
|
|
180
|
+
await ws.filters.add('A1:C10');
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Low-Level Access
|
|
184
|
+
|
|
185
|
+
Drop to the raw compute bridge when the high-level API isn't enough:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
const bridge = wb.context.computeBridge;
|
|
189
|
+
const sheetId = (await bridge.getAllSheetIds())[0];
|
|
190
|
+
const result = await bridge.setCell(sheetId, crypto.randomUUID(), 0, 0, '=1+1');
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Cleanup
|
|
194
|
+
|
|
195
|
+
**Always dispose when done** to avoid resource leaks:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
await wb.dispose();
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Interactive Script Runner
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# From sdk/
|
|
205
|
+
node run.cjs # runs examples/hello.ts
|
|
206
|
+
node run.cjs examples/explore-api.ts # explore the full API
|
|
207
|
+
node run.cjs my-script.ts # run your own script
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Scripts export a default async function that receives a ready `Workbook`:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
import type { Workbook } from '../src/index';
|
|
214
|
+
|
|
215
|
+
export default async function (wb: Workbook) {
|
|
216
|
+
const ws = wb.activeSheet;
|
|
217
|
+
await ws.setCell('A1', 'Hello from SDK!');
|
|
218
|
+
console.log(await ws.getValue('A1'));
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Backward Compatibility
|
|
223
|
+
|
|
224
|
+
`HeadlessEngine` and `createHeadlessEngine()` are preserved for existing consumers:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
import { createHeadlessEngine } from '@mog/sdk';
|
|
228
|
+
|
|
229
|
+
const engine = await createHeadlessEngine({ computeAddon: addon });
|
|
230
|
+
const ws = engine.workbook.getActiveSheet();
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
New code should use `createWorkbook()` directly.
|
|
234
|
+
|
|
235
|
+
## Architecture
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
createWorkbook()
|
|
239
|
+
└─ DocumentFactory.create({ environment: 'headless' })
|
|
240
|
+
└─ DocumentLifecycleSystem (XState machine)
|
|
241
|
+
├─ createTransport() → auto-detects NAPI
|
|
242
|
+
│ ├─ LazyNapiTransport → compute-core-napi.node (Rust)
|
|
243
|
+
│ └─ CompositeTransport → xlsx-parser-napi.node (optional)
|
|
244
|
+
├─ ComputeBridge (async cell ops, recalc)
|
|
245
|
+
└─ DocumentContext (kernel services, event bus)
|
|
246
|
+
└─ Workbook / Worksheet (unified API)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The SDK boots the **same kernel** used by the browser app, with headless stubs for browser-only services (Canvas, DOM, IndexedDB). Transport goes through napi-rs directly to Rust — no WASM, no IPC, full native speed.
|
|
250
|
+
|
|
251
|
+
## Prerequisites
|
|
252
|
+
|
|
253
|
+
- **Node.js** 20+
|
|
254
|
+
- **pnpm** 10+
|
|
255
|
+
- **Native addon** built: `cd compute-core-napi && pnpm build`
|
|
256
|
+
|
|
257
|
+
To check if the addon is current:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
node check-addon.cjs
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Example Scripts
|
|
264
|
+
|
|
265
|
+
| Script | What it does |
|
|
266
|
+
|--------|-------------|
|
|
267
|
+
| `examples/hello.ts` | Boot, write cells, read back |
|
|
268
|
+
| `examples/explore-api.ts` | Write dataset, read, search, summarize |
|
|
269
|
+
| `examples/getrange-test.ts` | Validate batch_get_cells: formulas, empty cells, identity |
|
|
270
|
+
| `examples/test-interactive.ts` | Interactive API exploration |
|
|
271
|
+
| `examples/test-usedrange.ts` | Used range detection |
|
|
272
|
+
|
|
273
|
+
## Known Issues
|
|
274
|
+
|
|
275
|
+
**Console noise on boot**: `[SchemaValidationBridge]` errors during startup are harmless — the schema bridge tries to populate caches before the compute bridge is fully ready.
|
|
276
|
+
|
|
277
|
+
**IndexedDB error on dispose**: `indexedDB is not defined` appears on shutdown. The kernel tries to save to IndexedDB (browser API) which doesn't exist in Node.js. Caught and harmless.
|