Skip to content

Commit 6cdd93f

Browse files
feat: node updates
Signed-off-by: Janos Sarusi-Kis <janossk@cisco.com>
1 parent 2e7486e commit 6cdd93f

File tree

5 files changed

+38
-242
lines changed

5 files changed

+38
-242
lines changed

data-plane/bindings/node/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SLIM Node.js Bindings
22

33
Node.js bindings for SLIM using UniFFI.
4+
Bindings generated with [uniffi-bindgen-node](https://github.com/livekit/uniffi-bindgen-node).
45

56
## Prerequisites
67

@@ -39,6 +40,36 @@ task example:alice # Run Alice receiver
3940
task example:bob # Run Bob sender
4041
```
4142

43+
## Build Process
44+
45+
The bindings generation includes patching to fix compatibility issues between `uniffi-bindgen-node` (code generator) and `uniffi-bindgen-react-native` (runtime library):
46+
47+
- **Naming fixes**: `FfiConverterBytes``FfiConverterArrayBuffer`
48+
- **Buffer wrapping**: Proper RustBuffer allocation for byte arrays
49+
- **Pointer conversions**: BigInt → Number for FFI calls
50+
- **Error handling**: Enhanced error extraction from Rust
51+
52+
## FFI Type Conversions
53+
54+
The generated bindings use `bigint` in TypeScript signatures for u64 types, but the underlying FFI layer returns JavaScript `number` types at runtime. Application code handles conversions at API boundaries:
55+
56+
```typescript
57+
// connectAsync returns a number at runtime, despite bigint type signature
58+
const connId = await service.connectAsync(config);
59+
60+
// Convert to BigInt when passing to methods expecting bigint
61+
await app.subscribeAsync(name, BigInt(connId));
62+
63+
// When using with methods that need number (for direct FFI calls)
64+
app.setRoute(name, Number(connId));
65+
```
66+
67+
**Key conversions:**
68+
- `connectAsync` returns `number` → convert to `BigInt()` for TypeScript bigint parameters
69+
- When calling FFI methods with u64 params → convert to `Number()` if passing bigint
70+
71+
This explicit conversion at API boundaries ensures type safety and makes FFI requirements visible.
72+
4273

4374
## Resources
4475

data-plane/bindings/node/examples/README.md

Lines changed: 0 additions & 199 deletions
This file was deleted.

data-plane/bindings/node/examples/common.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,12 @@ export async function createAndConnectApp(
6969
console.log(`[${app.id()}] 🔌 Connected to ${serverAddr} (conn ID: ${connId})`);
7070

7171
// Forward subscription to next node
72-
// Note: connId must be converted to BigInt for subscribeAsync
72+
// Note: Generated bindings expect bigint, but connectAsync returns number
73+
// Convert number -> bigint for the subscription call
7374
await app.subscribeAsync(appName, BigInt(connId));
7475
console.log(`[${app.id()}] ✅ Subscribed to sessions`);
7576

77+
// Return connId as bigint for consistency with TypeScript signatures
7678
return { app, connId: BigInt(connId), service };
7779
} catch (error: any) {
7880
let errorMsg = 'Unknown error';

data-plane/bindings/node/examples/point-to-point-alice.ts

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
* This example demonstrates the receiver side of a point-to-point messaging scenario.
88
* Alice listens for incoming sessions and receives messages from Bob.
99
*
10-
* Note: Sending replies is currently blocked by uniffi-bindgen-node limitations.
1110
*/
1211

1312
import { createAndConnectApp, logMessage, sleep, DEFAULT_SERVER_ENDPOINT, DEFAULT_SHARED_SECRET } from './common.js';
@@ -61,8 +60,7 @@ function parseArgs(): CliArgs {
6160
/**
6261
* Handle an incoming session
6362
* Receives messages until the session closes
64-
*
65-
* Note: Sending replies is blocked by uniffi-bindgen-node limitations
63+
6664
*/
6765
async function handleSession(app: any, session: any, instance: bigint): Promise<void> {
6866
const sessionId = session.sessionId();
@@ -79,29 +77,17 @@ async function handleSession(app: any, session: any, instance: bigint): Promise<
7977
const text = Buffer.from(payload).toString('utf-8');
8078
logMessage(instance, `📨 Received: ${text}`);
8179

82-
// try to send a reply using synchronous call
80+
// Send a reply
8381
try {
8482
session.publishToAndWait(receivedMsg.context, Buffer.from('Hello from Alice'), undefined, undefined);
8583
logMessage(instance, '📤 Sent reply: Hello from Alice');
8684
} catch (error: any) {
87-
// Debug: log the raw error structure to understand what we're getting
88-
console.log('[DEBUG] Raw error:', error);
89-
console.log('[DEBUG] Error type:', typeof error);
90-
console.log('[DEBUG] Error constructor:', error?.constructor?.name);
91-
console.log('[DEBUG] Is array:', Array.isArray(error));
92-
if (Array.isArray(error)) {
93-
console.log('[DEBUG] Array length:', error.length);
94-
console.log('[DEBUG] Array[0]:', error[0]);
95-
console.log('[DEBUG] Array[1]:', error[1]);
96-
}
97-
9885
// Error is returned as a tuple: ["SlimError", SlimError]
9986
// SlimError is a tagged union with different error types
10087
let errorMessage = 'Unknown error';
10188

10289
if (Array.isArray(error) && error.length === 2 && error[0] === 'SlimError' && error[1]) {
10390
const slimError = error[1];
104-
console.log('[DEBUG] SlimError object:', JSON.stringify(slimError, null, 2));
10591

10692
// Extract message based on error tag
10793
switch (slimError.tag) {
@@ -136,12 +122,9 @@ async function handleSession(app: any, session: any, instance: bigint): Promise<
136122
errorMessage = `${slimError.tag}: ${JSON.stringify(slimError)}`;
137123
}
138124
} else if (error?.message) {
139-
errorMessage = error.message;
125+
errorMessage = error.message || '(empty error message)';
140126
} else if (error instanceof Error) {
141127
errorMessage = `${error.name}: ${error.message}`;
142-
if (error.stack) {
143-
console.log('[DEBUG] Error stack:', error.stack);
144-
}
145128
} else {
146129
errorMessage = String(error);
147130
}

data-plane/bindings/node/examples/point-to-point-bob.ts

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,8 @@ async function runSender(args: CliArgs): Promise<void> {
104104
// Parse remote name
105105
const remoteName = splitId(args.remote);
106106

107-
// Debug connId type
108-
console.log('[DEBUG] connId value:', connId);
109-
console.log('[DEBUG] connId type:', typeof connId);
110-
111-
// Convert connId to number for FFI (ffi-rs expects number, not bigint)
107+
// Convert bigint -> number for FFI compatibility (ffi-rs requires number type)
112108
const connIdNum = Number(connId);
113-
console.log('[DEBUG] connIdNum value:', connIdNum);
114-
console.log('[DEBUG] connIdNum type:', typeof connIdNum);
115109

116110
logMessage(instance, '📍 Setting route to remote peer...');
117111

@@ -120,25 +114,10 @@ async function runSender(args: CliArgs): Promise<void> {
120114
app.setRoute(remoteName, connIdNum);
121115
logMessage(instance, '✅ Route established');
122116
} catch (error: any) {
123-
// Debug: log the raw error structure
124-
console.log('[DEBUG] setRoute error - raw:', error);
125-
console.log('[DEBUG] setRoute error - type:', typeof error);
126-
console.log('[DEBUG] setRoute error - constructor:', error?.constructor?.name);
127-
console.log('[DEBUG] setRoute error - is array:', Array.isArray(error));
128-
if (Array.isArray(error)) {
129-
console.log('[DEBUG] setRoute error - array length:', error.length);
130-
console.log('[DEBUG] setRoute error - array[0]:', error[0]);
131-
console.log('[DEBUG] setRoute error - array[1]:', error[1]);
132-
if (error[1]) {
133-
console.log('[DEBUG] setRoute error - SlimError object:', JSON.stringify(error[1], null, 2));
134-
}
135-
}
136-
137117
// Parse error properly
138118
let errorMsg = 'Unknown error';
139119
if (Array.isArray(error) && error.length === 2 && error[0] === 'SlimError' && error[1]) {
140120
const slimError = error[1];
141-
console.log('[DEBUG] SlimError tag:', slimError.tag);
142121
switch (slimError.tag) {
143122
case 'sendError':
144123
case 'sessionError':

0 commit comments

Comments
 (0)