Complete API reference for the Pocket Plugin SDK.
Plugin metadata structure:
interface Metadata {
name: string;
version: string;
description: string;
author: string;
license?: string;
runtime: 'wasm';
binary: string;
nodes: NodeDefinition[];
permissions: Permissions;
requirements: Requirements;
}Defines a node type exported by the plugin:
interface NodeDefinition {
type: string; // Unique node type identifier
category: string; // Category (e.g., 'transform', 'ai', 'data')
description: string; // Human-readable description
configSchema?: JSONSchema; // Configuration schema
inputSchema?: JSONSchema; // Input data schema
outputSchema?: JSONSchema; // Output data schema
examples?: Example[]; // Usage examples
}Security permissions required by the plugin:
interface Permissions {
memory?: string; // Max memory (e.g., "10MB")
timeout?: number; // Max execution time in ms
env?: string[]; // Allowed environment variables
filesystem?: string[]; // Allowed filesystem paths
network?: string[]; // Allowed network endpoints (future)
}Plugin requirements:
interface Requirements {
pocket: string; // Pocket version requirement (e.g., ">=1.0.0")
memory?: string; // Minimum memory requirement
cpu?: string; // CPU requirements (future)
}Main plugin class for registering nodes and metadata:
class Plugin {
readonly metadata: Metadata;
readonly nodes: Map<string, PluginNode>;
constructor(metadata: Metadata);
/**
* Register a node with the plugin
*/
register(node: PluginNode): void;
/**
* Get a registered node by type
*/
getNode(type: string): PluginNode | undefined;
/**
* Internal: Handle plugin calls from host
*/
_call(request: Request): Promise<Response>;
}const plugin = new Plugin({
name: 'my-plugin',
version: '1.0.0',
description: 'My awesome plugin',
author: 'Your Name',
runtime: 'wasm',
binary: 'plugin.wasm',
nodes: [], // Will be populated by register()
permissions: {
memory: '10MB',
timeout: 5000
},
requirements: {
pocket: '>=1.0.0'
}
});
plugin.register(new MyCustomNode());
plugin.register(new AnotherNode());
initializePlugin(plugin);Base class for implementing plugin nodes:
abstract class PluginNode<TInput = any, TOutput = any, TConfig = any> {
abstract readonly type: string;
abstract readonly category: string;
abstract readonly description: string;
readonly configSchema?: JSONSchema;
readonly inputSchema?: JSONSchema;
readonly outputSchema?: JSONSchema;
readonly examples?: Example[];
/**
* Preparation phase - validate inputs and prepare data
*/
abstract prep(
input: TInput,
config: TConfig,
store: Store
): Promise<any>;
/**
* Execution phase - core business logic
*/
abstract exec(
prepResult: any,
config: TConfig
): Promise<TOutput>;
/**
* Post-processing phase - handle results and routing
*/
abstract post(
input: TInput,
prepResult: any,
execResult: TOutput,
config: TConfig,
store: Store
): Promise<{
output: TOutput;
next: string;
}>;
}interface MyInput {
data: string[];
options: {
mode: 'fast' | 'accurate';
};
}
interface MyOutput {
result: string[];
metadata: {
processedCount: number;
duration: number;
};
}
interface MyConfig {
threshold: number;
enableCache: boolean;
}
class MyNode extends PluginNode<MyInput, MyOutput, MyConfig> {
readonly type = 'my-processor';
readonly category = 'transform';
readonly description = 'Processes data with custom logic';
readonly configSchema = {
type: 'object',
properties: {
threshold: {
type: 'number',
default: 0.5,
minimum: 0,
maximum: 1
},
enableCache: {
type: 'boolean',
default: true
}
}
};
async prep(input: MyInput, config: MyConfig, store: Store) {
// Validate input
if (!Array.isArray(input.data)) {
throw new Error('Input data must be an array');
}
// Check cache if enabled
if (config.enableCache) {
const cached = store.get('cache:' + JSON.stringify(input));
if (cached) {
return { cached: true, data: cached };
}
}
// Prepare data for processing
return {
cached: false,
data: input.data.filter(item => item.length > 0),
startTime: Date.now()
};
}
async exec(prepResult: any, config: MyConfig): Promise<MyOutput> {
const { data, startTime } = prepResult;
// Core processing logic
const result = data.map(item =>
processItem(item, config.threshold)
);
return {
result,
metadata: {
processedCount: result.length,
duration: Date.now() - startTime
}
};
}
async post(
input: MyInput,
prepResult: any,
execResult: MyOutput,
config: MyConfig,
store: Store
) {
// Cache result if enabled
if (config.enableCache && !prepResult.cached) {
store.set('cache:' + JSON.stringify(input), execResult);
}
// Determine routing
const next = execResult.metadata.processedCount > 0
? 'success'
: 'empty';
return {
output: execResult,
next
};
}
}Key-value store for plugin state:
interface Store {
/**
* Get a value by key
*/
get(key: string): any | undefined;
/**
* Set a value
*/
set(key: string, value: any): void;
/**
* Delete a key
*/
delete(key: string): boolean;
/**
* Clear all data
*/
clear(): void;
/**
* Check if key exists
*/
has(key: string): boolean;
/**
* Get all keys
*/
keys(): string[];
}Internal request structure from host:
interface Request {
node: string; // Node type
function: 'prep' | 'exec' | 'post';
config?: any; // Node configuration
input?: any; // Input for prep
prepResult?: any; // Result from prep (for exec)
execResult?: any; // Result from exec (for post)
}Internal response structure to host:
interface Response {
success: boolean;
output?: any;
error?: string;
next?: string; // Routing decision from post
}Standard JSON Schema for validation:
interface JSONSchema {
type?: string | string[];
properties?: { [key: string]: JSONSchema };
items?: JSONSchema;
required?: string[];
enum?: any[];
minimum?: number;
maximum?: number;
minLength?: number;
maxLength?: number;
pattern?: string;
default?: any;
description?: string;
// ... other JSON Schema properties
}Plugin example structure:
interface Example {
name: string;
description?: string;
input: any;
config?: any;
output: any;
next?: string;
}Functions for WASM memory management:
Allocate memory for data transfer:
export function __pocket_alloc(size: number): number- Parameters:
size: Number of bytes to allocate
- Returns: Pointer to allocated memory
- Usage: Called by host to allocate memory for passing data
Free allocated memory:
export function __pocket_free(ptr: number, size: number): void- Parameters:
ptr: Pointer to memory to freesize: Size of memory block
- Usage: Called by host to free memory after use
Main entry point for plugin calls:
export function __pocket_call(ptr: number, size: number): number- Parameters:
ptr: Pointer to request datasize: Size of request data
- Returns: Pointer to response data
- Usage: Called by host to invoke plugin functionality
Initialize the plugin for the host environment:
function initializePlugin(plugin: Plugin): void- Parameters:
plugin: The plugin instance to initialize
- Usage: Must be called after registering all nodes
Deep clone an object:
function deepClone<T>(obj: T): T- Parameters:
obj: Object to clone
- Returns: Deep copy of the object
Deep merge objects:
function deepMerge<T extends object>(target: T, ...sources: Partial<T>[]): T- Parameters:
target: Target objectsources: Objects to merge into target
- Returns: Merged object
Retry an operation with exponential backoff:
async function retry<T>(
fn: () => Promise<T>,
options?: {
maxAttempts?: number;
delay?: number;
backoff?: number;
onError?: (error: Error, attempt: number) => void;
}
): Promise<T>- Parameters:
fn: Function to retryoptions: Retry configuration
- Returns: Result of successful operation
Create a debounced function:
function debounce<T extends (...args: any[]) => any>(
fn: T,
delay: number
): T & { cancel: () => void }- Parameters:
fn: Function to debouncedelay: Delay in milliseconds
- Returns: Debounced function with cancel method
Create a throttled function:
function throttle<T extends (...args: any[]) => any>(
fn: T,
limit: number
): T- Parameters:
fn: Function to throttlelimit: Minimum time between calls in ms
- Returns: Throttled function
Memoize a function:
function memoize<T extends (...args: any[]) => any>(
fn: T,
keyFn?: (...args: Parameters<T>) => string
): T- Parameters:
fn: Function to memoizekeyFn: Optional function to generate cache key
- Returns: Memoized function
Base error class for plugin errors:
class PluginError extends Error {
constructor(
message: string,
public code?: string,
public details?: any
) {
super(message);
this.name = 'PluginError';
}
}INVALID_INPUT: Input validation failedINVALID_CONFIG: Configuration validation failedTIMEOUT: Operation timed outMEMORY_LIMIT: Memory limit exceededPERMISSION_DENIED: Permission not grantedNOT_FOUND: Resource not foundINTERNAL_ERROR: Internal plugin error
async prep(input: Input, config: Config, store: Store) {
try {
if (!input.data) {
throw new PluginError(
'Input data is required',
'INVALID_INPUT',
{ field: 'data' }
);
}
// Processing...
} catch (error) {
if (error instanceof PluginError) {
throw error;
}
// Wrap unknown errors
throw new PluginError(
'Unexpected error in prep phase',
'INTERNAL_ERROR',
{ originalError: error.message }
);
}
}- Type Safety: Use TypeScript generics for type-safe implementations
- Validation: Validate all inputs in the prep phase
- Pure Exec: Keep exec phase pure with no side effects
- Error Messages: Provide clear, actionable error messages
- Resource Cleanup: Clean up resources in error cases
- Documentation: Document all config options and schemas
- Examples: Provide comprehensive examples
- Testing: Test all phases independently