Skip to content

Commit adb494b

Browse files
committed
Add JavaScript SDK with zero-copy wrappers
- Full-featured SDK mirroring Python rayforce-py wrapper - Zero-copy ArrayBuffer views over native Rayforce vectors - Scalar types: B8, U8, I16, I32, I64, F64, Symbol, Date, Time, Timestamp, GUID - Container types: Vector, List, Dict, Table with typed array access - Query builder API: SelectQuery, UpdateQuery, InsertQuery, joins - TypeScript definitions (rayforce.sdk.d.ts) - UMD bundle for CDN/script tag usage - Interactive demo page with REPL, SDK demos, and table viewer - Lucide icons and Fira Code/IBM Plex fonts - URL parameter support (?expr=...) for shareable expressions
1 parent f1a0f49 commit adb494b

11 files changed

Lines changed: 5372 additions & 588 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,20 @@ jobs:
4343
- name: Prepare deployment
4444
run: |
4545
mkdir -p _site
46+
# Copy all dist files (WASM, JS, SDK)
4647
cp dist/rayforce.js _site/
4748
cp dist/rayforce.wasm _site/
49+
cp dist/rayforce.sdk.js _site/
50+
cp dist/rayforce.sdk.d.ts _site/
51+
cp dist/rayforce.umd.js _site/
52+
cp dist/index.js _site/
53+
# Copy examples
4854
cp examples/index.html _site/
4955
cp examples/favicon.ico _site/
5056
cp -r examples/assets _site/
57+
# Fix import paths for flat structure
5158
sed -i 's|../dist/rayforce.js|./rayforce.js|g' _site/index.html
59+
sed -i 's|../dist/rayforce.sdk.js|./rayforce.sdk.js|g' _site/index.html
5260
5361
- name: Upload artifact
5462
uses: actions/upload-pages-artifact@v3

CLAUDE.md

Lines changed: 117 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
44

55
## Project Overview
66

7-
rayforce-wasm provides WebAssembly bindings for RayforceDB, a high-performance database. It compiles the native RayforceDB C runtime to WebAssembly using Emscripten, enabling RayforceDB to run in web browsers and Node.js environments.
7+
rayforce-wasm provides WebAssembly bindings and a full JavaScript SDK for RayforceDB, a high-performance columnar database. The SDK offers zero-copy TypedArray views over native Rayforce vectors for maximum performance.
88

99
## Build Commands
1010

@@ -21,7 +21,7 @@ make app
2121
# Development build from local ../rayforce sources
2222
make dev
2323

24-
# Build optimized WASM (ES6 module)
24+
# Build optimized WASM with SDK (ES6 module)
2525
make wasm
2626

2727
# Build standalone version with preloaded examples
@@ -47,7 +47,7 @@ make clean-all
4747
- **Makefile** - Main build orchestration
4848
- `pull` - Clones RayforceDB from GitHub to `build/rayforce-c/`
4949
- `sync` - Copies from local `../rayforce/` for development
50-
- `wasm` - Compiles to ES6 WASM module
50+
- `wasm` - Compiles to ES6 WASM module + copies SDK files
5151
- `wasm-standalone` - Includes preloaded example files
5252
- `wasm-debug` - Debug build with assertions and safe heap
5353

@@ -59,75 +59,141 @@ rayforce-wasm/
5959
├── CLAUDE.md # This file
6060
├── README.md # Documentation
6161
├── src/
62-
│ └── rayforce/ # Copied C sources from RayforceDB
62+
│ ├── main.c # WASM entry point with all exports
63+
│ ├── rayforce.sdk.js # ES6 SDK module
64+
│ ├── rayforce.umd.js # UMD bundle for CDN
65+
│ └── index.js # Main entry point
6366
├── build/
6467
│ ├── rayforce-c/ # Cloned RayforceDB repo
6568
│ ├── obj/ # Compiled object files
6669
│ └── librayforce.a # Static library
6770
├── dist/
6871
│ ├── rayforce.js # ES6 WASM module loader
6972
│ ├── rayforce.wasm # WebAssembly binary
70-
│ └── rayforce-debug.js # Debug version
73+
│ ├── rayforce.sdk.js # SDK module
74+
│ ├── rayforce.umd.js # UMD bundle
75+
│ └── index.js # Entry point
7176
└── examples/ # Usage examples
7277
```
7378

74-
### Exported Functions
79+
## SDK Usage
7580

76-
The WASM module exports these C functions for JavaScript:
77-
78-
- `_main` - Entry point
79-
- `_version_str` - Get version string
80-
- `_null` - Create null object
81-
- `_drop_obj` - Free object memory
82-
- `_clone_obj` - Clone an object
83-
- `_eval_str` - Evaluate a RayforceDB expression (simple, no source tracking)
84-
- `_eval_cmd` - Evaluate with source tracking for proper error locations (preferred)
85-
- `_get_cmd_counter` - Get current command counter
86-
- `_reset_cmd_counter` - Reset command counter
87-
- `_obj_fmt` - Format object to string
88-
- `_strof_obj` - Convert object to string representation
89-
90-
#### Error Location Tracking
91-
92-
The `_eval_cmd` function provides proper error location tracking similar to the native RayforceDB REPL:
81+
### ES6 Module
9382

9483
```javascript
95-
// Preferred: Evaluate with source tracking
96-
const result = rayforce.ccall('eval_cmd', 'number', ['string', 'string'], [code, 'myfile.ray']);
97-
98-
// Errors will show source location like:
99-
// ** [E002] error: object evaluation failed
100-
// ╭──[1]──┬ myfile.ray:1..1 in function: @anonymous
101-
// │ 1 │ (+ 1 undefined-var)
102-
// │ ┴ ~~~~~~~~~~~~~ undefined symbol: 'undefined-var
103-
```
84+
import { createRayforceSDK, Types } from './dist/rayforce.sdk.js';
10485

105-
### JavaScript Interface
86+
// Initialize WASM first
87+
const createRayforce = (await import('./dist/rayforce.js')).default;
88+
const wasm = await createRayforce();
10689

107-
The module is built with:
108-
- `MODULARIZE=1` - Factory function pattern
109-
- `EXPORT_ES6=1` - ES6 module syntax
110-
- `EXPORT_NAME="createRayforce"` - Factory function name
111-
- `ALLOW_MEMORY_GROWTH=1` - Dynamic memory allocation
90+
// Create SDK instance
91+
const rf = createRayforceSDK(wasm);
11292

113-
Usage:
114-
```javascript
115-
import createRayforce from './rayforce.js';
93+
// Evaluate expressions
94+
const result = rf.eval('(+ 1 2 3)');
95+
console.log(result.toJS()); // 6
11696

117-
const rayforce = await createRayforce();
97+
// Zero-copy vector operations
98+
const vec = rf.vector(Types.I64, [1, 2, 3, 4, 5]);
99+
const view = vec.typedArray; // BigInt64Array - zero copy!
100+
view[0] = 100n; // Mutate in place
118101

119-
// Preferred: Use eval_cmd for proper error tracking
120-
const result = rayforce.ccall('eval_cmd', 'number', ['string', 'string'], ['(+ 1 2 3)', 'repl']);
121-
const formatted = rayforce.ccall('strof_obj', 'string', ['number'], [result]);
122-
console.log(formatted); // 6
102+
// Create tables
103+
const table = rf.table({
104+
id: [1, 2, 3],
105+
name: ['Alice', 'Bob', 'Carol'],
106+
score: [95.5, 87.3, 92.1]
107+
});
123108

124-
// Clean up
125-
rayforce.ccall('drop_obj', null, ['number'], [result]);
109+
console.log(table.toRows());
110+
// [{ id: 1, name: 'Alice', score: 95.5 }, ...]
111+
```
112+
113+
### CDN/Script Tag
126114

127-
// Simple evaluation (no source tracking)
128-
const simple = rayforce.ccall('eval_str', 'number', ['string'], ['1+2+3']);
115+
```html
116+
<script src="https://cdn.../rayforce.umd.js"></script>
117+
<script>
118+
Rayforce.init({ wasmPath: './rayforce.js' }).then(rf => {
119+
const result = rf.eval('(sum (til 100))');
120+
console.log(result.toJS()); // 4950
121+
});
122+
</script>
129123
```
130124

125+
## Type System
126+
127+
### Type Codes (matching Python bindings)
128+
129+
| Type | Code | TypedArray | Description |
130+
|------|------|------------|-------------|
131+
| LIST | 0 | - | Mixed-type container |
132+
| B8 | 1 | Int8Array | Boolean |
133+
| U8 | 2 | Uint8Array | Unsigned byte |
134+
| I16 | 3 | Int16Array | 16-bit integer |
135+
| I32 | 4 | Int32Array | 32-bit integer |
136+
| I64 | 5 | BigInt64Array | 64-bit integer |
137+
| SYMBOL | 6 | BigInt64Array | Interned string |
138+
| DATE | 7 | Int32Array | Days since 2000-01-01 |
139+
| TIME | 8 | Int32Array | Milliseconds since midnight |
140+
| TIMESTAMP | 9 | BigInt64Array | Nanoseconds since 2000-01-01 |
141+
| F64 | 10 | Float64Array | 64-bit float |
142+
| GUID | 11 | - | 128-bit UUID |
143+
| C8 | 12 | Uint8Array | Character/String |
144+
| TABLE | 98 | - | Table |
145+
| DICT | 99 | - | Dictionary |
146+
147+
### Atoms vs Vectors
148+
149+
- **Atoms (scalars)**: Have negative type codes (e.g., -5 for I64 scalar)
150+
- **Vectors**: Have positive type codes (e.g., 5 for I64 vector)
151+
152+
## Exported WASM Functions
153+
154+
### Core
155+
- `eval_cmd(code, sourceName)` - Evaluate with source tracking
156+
- `eval_str(code)` - Simple evaluation
157+
- `strof_obj(ptr)` - Format object to string
158+
- `drop_obj(ptr)` - Free object memory
159+
- `clone_obj(ptr)` - Clone object
160+
161+
### Type Introspection
162+
- `get_obj_type(ptr)` - Get type code
163+
- `get_obj_len(ptr)` - Get length
164+
- `is_obj_atom(ptr)` - Check if scalar
165+
- `is_obj_vector(ptr)` - Check if vector
166+
- `is_obj_null(ptr)` - Check if null
167+
- `is_obj_error(ptr)` - Check if error
168+
169+
### Memory Access (Zero-Copy)
170+
- `get_data_ptr(ptr)` - Get pointer to data array
171+
- `get_element_size(type)` - Get byte size of element
172+
- `get_data_byte_size(ptr)` - Get total data size
173+
174+
### Constructors
175+
- `init_b8`, `init_u8`, `init_c8`, `init_i16`, `init_i32`, `init_i64`, `init_f64`
176+
- `init_date`, `init_time`, `init_timestamp`
177+
- `init_symbol_str`, `init_string_str`
178+
- `init_vector`, `init_list`, `init_dict`, `init_table`
179+
180+
### Readers
181+
- `read_b8`, `read_u8`, `read_c8`, `read_i16`, `read_i32`, `read_i64`, `read_f64`
182+
- `read_date`, `read_time`, `read_timestamp`
183+
- `read_symbol_id`, `symbol_to_str`
184+
185+
### Vector Operations
186+
- `vec_at_idx`, `vec_set_idx`, `vec_push`, `vec_insert`, `vec_resize`
187+
- `fill_i64_vec`, `fill_i32_vec`, `fill_f64_vec`
188+
189+
### Container Operations
190+
- `dict_keys`, `dict_vals`, `dict_get`
191+
- `table_keys`, `table_vals`, `table_col`, `table_row`, `table_count`
192+
193+
### Query Operations
194+
- `query_select`, `query_update`
195+
- `table_insert`, `table_upsert`
196+
131197
## Build Flags
132198

133199
### Release Build
@@ -149,7 +215,7 @@ const simple = rayforce.ccall('eval_str', 'number', ['string'], ['1+2+3']);
149215

150216
1. Make changes in `../rayforce/` (main RayforceDB repo)
151217
2. Run `make dev` to sync and build
152-
3. Test with `make serve` and open browser
218+
3. Test with `make serve` and open browser to examples/
153219
4. For production: `make app` builds from fresh GitHub clone
154220

155221
## Platform Notes
@@ -158,4 +224,3 @@ const simple = rayforce.ccall('eval_str', 'number', ['string'], ['1+2+3']);
158224
- `emcc` and `emar` must be available
159225
- Built WASM requires CORS headers for cross-origin usage
160226
- Use `make serve` for local testing (handles CORS)
161-

0 commit comments

Comments
 (0)