- Replace docs.ixt.dev with www.ixtaat.com - Fix 'GitHub:' label to 'Repository:' for Forgejo URL Part of comprehensive URL standardization across IXT codebase. |
||
|---|---|---|
| examples | ||
| .gitignore | ||
| BIDIRECTIONAL_IPC_GUIDE.md | ||
| CHANGELOG.md | ||
| IMPLEMENTATION_SUMMARY.md | ||
| ixt-process-module.js | ||
| LICENSE | ||
| package.json | ||
| PYTHON_TO_NODEJS.md | ||
| QUICKREF.md | ||
| README.md | ||
| RELEASE_NOTES.md | ||
| test-services.js | ||
IXT Node.js SDK
Node.js SDK for building IXT modules that run as separate processes and communicate with the IXT runtime via IPC.
Features
- ✅ Simple base class for module development
- ✅ Automatic IPC connection handling
- ✅ JSON message parsing and framing
- ✅ Lifecycle management (init, handle, shutdown, health_check)
- ✅ Base64 payload encoding/decoding helpers
- ✅ Error handling and logging
- ✅ Zero external dependencies (Node.js 14+)
- ✅ Full async/await support
- ✅ NEW: Bidirectional IPC Services
- MessageBus: Send messages to other modules
- KV Store: Persist and retrieve data (get, set, delete, exists)
- Logging: Send structured logs to runtime
Installation
# From source
cd sdk/ixt-sdk-nodejs
npm install
# Or copy ixt-process-module.js to your project
cp sdk/ixt-sdk-nodejs/ixt-process-module.js my-module/
Quick Start
1. Create Your Module
#!/usr/bin/env node
const { IxtProcessModule } = require('./ixt-process-module');
class MyModule extends IxtProcessModule {
async onInit(config) {
// Initialize module with configuration
this.dbUrl = config.database_url || 'sqlite://data.db';
this.log(`Connecting to database: ${this.dbUrl}`);
// Initialize your resources here
}
async onHandle(message, context) {
// Handle incoming messages
const topic = message.topic;
const payloadData = this.decodePayload(message.payload);
if (topic === 'my.service.process') {
const result = await this.processData(payloadData);
return this.success({
result: this.encodePayload(result)
});
} else if (topic === 'my.service.query') {
const data = await this.queryData(payloadData);
return this.success({
data: this.encodePayload(data)
});
} else {
return this.error('UNKNOWN_TOPIC', `Unknown topic: ${topic}`);
}
}
async onShutdown() {
// Clean up resources
this.log('Shutting down module');
// Close connections, save state, etc.
}
async onHealthCheck() {
// Report health status (optional)
return {
healthy: true,
database_connected: await this.checkDbConnection(),
requests_processed: this.requestCount
};
}
}
if (require.main === module) {
new MyModule().run();
}
2. Create Manifest
# manifest.toml
schema_version = "3.0"
[module]
name = "my-module"
version = "1.0.0"
description = "My IXT module"
author = "Your Name"
license = "MIT"
runtime_targets = ["Process"]
[provides]
services = ["my.service.process", "my.service.query"]
[config]
database_url = { type = "string", default = "sqlite://data.db" }
3. Run Your Module
The IXT runtime will spawn your module automatically. For development testing:
# Runtime sets these environment variables:
export IXT_IPC_SOCKET=/tmp/ixt-module-abc123.sock
export IXT_MODULE_ID=my-module:1.0.0/instance-001
# Run your module
node my-module.js
API Reference
IxtProcessModule Base Class
Abstract base class for IXT modules. Implement these methods:
onInit(config)
Initialize your module with configuration from the manifest.
async onInit(config) {
this.setting = config.my_setting || 'default';
// Initialize resources, connect to services, etc.
}
onHandle(message, context)
Handle incoming messages. This is where your business logic goes.
async onHandle(message, context) {
const topic = message.topic;
const payload = this.decodePayload(message.payload);
// Process the message
const result = await this.process(payload);
// Return success or error
return this.success({ result: this.encodePayload(result) });
}
Message structure:
{
topic: 'service.name', // Service topic
payload: 'base64-encoded-data', // Base64-encoded payload
priority: 1, // Priority level
ttl_ms: 5000 // Time-to-live in milliseconds
}
Context structure:
{
trace_id: 'unique-trace-id', // Distributed tracing ID
source_module: 'caller:1.0.0', // Calling module
timestamp: 1234567890 // Unix timestamp
}
onShutdown()
Gracefully shutdown your module.
async onShutdown() {
await this.db.close();
await this.saveState();
}
onHealthCheck() (optional)
Report health status. Default returns {healthy: true}.
async onHealthCheck() {
return {
healthy: await this.isHealthy(),
uptime_seconds: process.uptime()
};
}
Helper Methods
success(data)
Create a success response.
return this.success({ result: 42 });
error(code, message, details)
Create an error response.
return this.error('INVALID_INPUT', 'Missing required field: name');
encodePayload(data)
Encode binary data as base64 string.
const encoded = this.encodePayload(Buffer.from("hello world"));
decodePayload(payload)
Decode base64 payload to Buffer.
const data = this.decodePayload(message.payload);
const text = data.toString('utf8');
log(message)
Log informational message (to stderr, captured by runtime).
this.log(`Processing request ${requestId}`);
logError(message)
Log error message.
this.logError(`Failed to connect: ${error}`);
Bidirectional IPC Services
The SDK provides methods to interact with runtime services from within your module.
MessageBus Service
Send messages to other modules via the MessageBus.
sendMessage(topic, payload, priority, ttlMs)
Send a message to another module.
const response = await this.sendMessage(
'notifications.send', // Topic
Buffer.from(JSON.stringify({ // Payload (binary)
user_id: 123,
message: 'Hello!'
})),
2, // Priority: 1=low, 2=normal, 3=high, 4=critical
5000 // TTL in milliseconds (optional)
);
if (response.success) {
this.log('Message sent successfully');
}
Parameters:
topic(string): Message topic (e.g., "user.created", "order.process")payload(Buffer): Binary message payloadpriority(number): Message priority (1=low, 2=normal, 3=high, 4=critical), default: 1ttlMs(number, optional): Time-to-live in milliseconds
Returns: Promise