
Security News
npm v12 Ships With Install Scripts Off by Default, Begins Deprecating 2FA-Bypass Tokens
npm v12 is generally available, turning install scripts off by default and beginning the deprecation of 2FA-bypass publishing tokens.
@wiredwp/robinpath
Advanced tools
RobinPath - A lightweight, fast, and easy-to-use scripting language for automation and data processing.
A scripting language interpreter with a REPL interface and built-in modules for math, strings, JSON, time, arrays, and more.
Install RobinPath as a dependency in your project:
npm i @wiredwp/robinpath
Import and create a RobinPath instance to execute scripts in your application:
import { RobinPath } from '@wiredwp/robinpath';
// Create an interpreter instance
const rp = new RobinPath();
// Execute a script
const result = await rp.executeScript(`
add 10 20
multiply $ 2
`);
console.log('Result:', result); // 60
Use executeLine() for REPL-like behavior where state persists between calls:
const rp = new RobinPath();
// First line - sets $result
await rp.executeLine('$result = add 10 20');
console.log(rp.getLastValue()); // 30
// Second line - uses previous result
await rp.executeLine('multiply $result 2');
console.log(rp.getLastValue()); // 60
Get and set variables programmatically:
const rp = new RobinPath();
// Set a variable from JavaScript
rp.setVariable('name', 'Alice');
rp.setVariable('age', 25);
// Execute script that uses the variable
await rp.executeScript(`
log "Hello" $name
log "Age:" $age
`);
// Get a variable value
const name = rp.getVariable('name');
console.log(name); // "Alice"
Create isolated execution contexts with threads:
const rp = new RobinPath({ threadControl: true });
// Create a new thread
const thread1 = rp.createThread('user-123');
await thread1.executeScript('$count = 10');
// Create another thread with separate variables
const thread2 = rp.createThread('user-456');
await thread2.executeScript('$count = 20');
// Each thread maintains its own state
console.log(thread1.getVariable('count')); // 10
console.log(thread2.getVariable('count')); // 20
// Switch between threads
rp.useThread('user-123');
console.log(rp.currentThread?.getVariable('count')); // 10
Extend RobinPath with your own builtin functions:
const rp = new RobinPath();
// Register a simple builtin
rp.registerBuiltin('greet', (args) => {
const name = String(args[0] ?? 'World');
return `Hello, ${name}!`;
});
// Use it in scripts
await rp.executeScript('greet "Alice"');
console.log(rp.getLastValue()); // "Hello, Alice!"
Create and register custom modules:
const rp = new RobinPath();
// Register module functions
rp.registerModule('myapp', {
process: (args) => {
const data = args[0];
// Process data...
return processedData;
},
validate: (args) => {
const input = args[0];
return isValid(input);
}
});
// Register function metadata for documentation
rp.registerModuleFunctionMeta('myapp', 'process', {
description: 'Processes input data',
parameters: [
{
name: 'data',
dataType: 'object',
description: 'Data to process',
formInputType: 'json',
required: true
}
],
returnType: 'object',
returnDescription: 'Processed data'
});
// Register module-level metadata
rp.registerModuleInfo('myapp', {
description: 'Custom application module',
methods: ['process', 'validate']
});
// Use in scripts
await rp.executeScript(`
use myapp
myapp.process $data
`);
Query available commands for autocomplete or help:
const rp = new RobinPath();
const commands = rp.getAvailableCommands();
console.log(commands.native); // Language keywords (if, def, etc.)
console.log(commands.builtin); // Root-level builtins
console.log(commands.modules); // Available modules
console.log(commands.moduleFunctions); // Module.function names
console.log(commands.userFunctions); // User-defined functions
Get the AST with execution state for debugging or visualization:
const rp = new RobinPath({ threadControl: true });
const thread = rp.createThread('debug');
const script = `
add 5 5
$result = $
if $result > 5
multiply $result 2
endif
`;
const astResult = await thread.getASTWithState(script);
console.log(astResult.ast); // AST with lastValue at each node
console.log(astResult.variables); // Thread and global variables
console.log(astResult.lastValue); // Final result
console.log(astResult.callStack); // Call stack frames
Check if a script needs more input (useful for multi-line input):
const rp = new RobinPath();
const check1 = rp.needsMoreInput('if $x > 5');
console.log(check1); // { needsMore: true, waitingFor: 'endif' }
const check2 = rp.needsMoreInput('if $x > 5\n log "yes"\nendif');
console.log(check2); // { needsMore: false }
Handle errors from script execution:
const rp = new RobinPath();
try {
await rp.executeScript('unknown_function 123');
} catch (error) {
console.error('Script error:', error.message);
// "Unknown function: unknown_function"
}
Install globally to use the robinpath command:
npm i -g @wiredwp/robinpath
Or use it directly with npx:
npx @wiredwp/robinpath
Start the interactive REPL:
robinpath
Or if installed locally:
npm run cli
This will start an interactive session where you can type commands and see results immediately.
help or .help - Show help messageexit, quit, .exit, .quit - Exit the REPLclear or .clear - Clear the screen.. - Show all available commands as JSONMulti-line Blocks: The REPL automatically detects incomplete blocks and waits for completion:
> if $x > 5
... log "yes"
... endif
Backslash Line Continuation:
Use \ at the end of a line to continue the command on the next line:
> log "this is a very long message " \
... "that continues on the next line"
The backslash continuation works with any statement type and can be chained across multiple lines.
Thread Management: When thread control is enabled, the prompt shows the current thread and module:
default@math> add 5 5
10
default@math> use clear
Cleared module context
default> thread list
Threads:
- default (current)
- user-123
default> thread use user-123
Switched to thread: user-123
user-123>
Module Context:
The prompt shows the current module when using use:
> use math
Using module: math
default@math> add 5 5
10
default@math> use clear
Cleared module context
default>
Commands are executed by typing the command name followed by arguments:
add 10 20
log "Hello, World!"
multiply 5 3
Variables are prefixed with $:
$name = "Alice"
$age = 25
log $name $age
Use $ to reference the last computed value:
add 10 20
multiply $ 2 # Uses 30 (result of add)
log $ # Prints 60
Assign the last value to a variable by simply referencing it:
add 5 3
$sum # Assigns 8 to $sum
log $sum # Prints 8
Assign the value of one variable to another:
$city = "New York"
$city2 = $city # Copies "New York" to $city2
log $city2 # Prints "New York"
$number1 = 42
$number2 = $number1 # Copies 42 to $number2
$number3 = $number2 # Can chain assignments
RobinPath supports accessing object properties and array elements directly using dot notation and bracket notation:
Property Access:
json.parse '{"name": "John", "age": 30, "address": {"city": "NYC"}}'
$user = $
# Access properties using dot notation
log $user.name # Prints "John"
log $user.age # Prints 30
log $user.address.city # Prints "NYC" (nested property access)
# Use in expressions
if $user.age >= 18
log "Adult"
endif
# Use as function arguments
math.add $user.age 5 # Adds 30 + 5 = 35
Array Indexing:
$arr = range 10 15
log $arr[0] # Prints 10 (first element)
log $arr[2] # Prints 12 (third element)
log $arr[5] # Prints 15 (last element)
# Out of bounds access returns null
log $arr[10] # Prints null
# Use in expressions
if $arr[0] == 10
log "First is 10"
endif
Combined Access: You can combine property access and array indexing:
json.parse '{"items": [{"name": "item1", "price": 10}, {"name": "item2", "price": 20}]}'
$data = $
log $data.items[0].name # Prints "item1"
log $data.items[0].price # Prints 10
log $data.items[1].name # Prints "item2"
# Assign to variables
$firstItem = $data.items[0].name
log $firstItem # Prints "item1"
Error Handling:
null or undefined throws an error: Cannot access property 'propertyName' of nullCannot access property 'propertyName' of <type>Cannot access index X of non-array valuenull (doesn't throw)Note: Assignment targets must be simple variable names. You cannot assign directly to attributes (e.g., $user.name = "Jane" is not supported). Use the set function from the Object module instead:
set $user "name" "Jane" # Use Object.set for attribute assignment
RobinPath includes several built-in reserved methods:
log - Output values:
log "Hello, World!"
log $name $age
log "Result:" $(add 5 5)
obj - Create objects using JSON5 syntax:
# Create empty object
obj
$empty = $
# Create object with JSON5 syntax (unquoted keys, trailing commas allowed)
obj '{name: "John", age: 30}'
$user = $
# Nested objects and arrays
obj '{nested: {key: "value"}, items: [1, 2, 3]}'
$data = $
# JSON5 features: unquoted keys, single quotes, trailing commas
obj '{unquoted: "works", singleQuotes: "also works", trailing: true,}'
$config = $
# Access properties
log $user.name # Prints "John"
log $user.age # Prints 30
log $data.nested.key # Prints "value"
log $data.items[0] # Prints 1
The obj command uses JSON5 syntax, which is more flexible than standard JSON:
array - Create arrays from arguments:
# Create empty array
array
$empty = $
# Create array with elements
array 1 2 3
$numbers = $
# Mixed types
array "hello" "world" 42 true
$mixed = $
# Access elements
log $numbers[0] # Prints 1
log $numbers[1] # Prints 2
log $mixed[0] # Prints "hello"
log $mixed[2] # Prints 42
# Use in expressions
array 10 20 30
$values = $
math.add $values[0] $values[1] # Adds 10 + 20 = 30
The array command creates an array from all its arguments. If called without arguments, it returns an empty array [].
assign - Assign a value to a variable (with optional fallback):
# Basic assignment
assign $myVar "hello"
assign $myVar 42
assign $myVar $sourceVar
# Assignment with fallback (3rd parameter used if 2nd is empty/null)
assign $result $maybeEmpty "default value"
assign $count $maybeNull 0
assign $name $maybeEmpty "Unknown"
# Fallback is only used when the value is:
# - null or undefined
# - empty string (after trimming)
# - empty array
# - empty object
empty - Clear/empty a variable:
$myVar = "some value"
empty $myVar
log $myVar # Prints null
$arr = range 1 5
empty $arr
log $arr # Prints null
fallback - Return variable value or fallback if empty/null:
# Return variable value or fallback
$maybeEmpty = null
fallback $maybeEmpty "default value" # Returns "default value"
$maybeEmpty = ""
fallback $maybeEmpty "Unknown" # Returns "Unknown"
$hasValue = "Alice"
fallback $hasValue "Unknown" # Returns "Alice" (fallback not used)
# Without fallback, returns the variable value (even if null)
$maybeEmpty = null
fallback $maybeEmpty # Returns null
The fallback command checks if a variable is empty/null and returns the fallback value if provided. A value is considered empty if it is:
null or undefinedmeta - Add metadata to variables or functions:
# Add metadata to a variable
$testVar = 100
meta $testVar description "A variable to store test values"
meta $testVar version 5
meta $testVar author "Test Author"
# Add metadata to a function
def calculate
math.add $1 $2
enddef
meta calculate description "A function to calculate sum"
meta calculate version 1
meta calculate category "math"
The meta command stores arbitrary key-value metadata for variables or functions. The metadata is stored separately and does not affect the variable or function itself.
getMeta - Retrieve metadata from variables or functions:
# Get all metadata for a variable
$testVar = 100
meta $testVar description "A variable"
meta $testVar version 5
getMeta $testVar
$allMeta = $ # Returns {description: "A variable", version: 5}
# Get specific metadata key
getMeta $testVar description # Returns "A variable"
getMeta $testVar version # Returns 5
getMeta $testVar nonexistent # Returns null
# Get all metadata for a function
def calculate
math.add $1 $2
enddef
meta calculate description "A function"
meta calculate version 1
getMeta calculate
$funcMeta = $ # Returns {description: "A function", version: 1}
# Get specific metadata key
getMeta calculate description # Returns "A function"
getMeta calculate version # Returns 1
The getMeta command retrieves metadata:
null if not found){} if no metadata existsclear - Clear the last return value ($):
# Clear the last value
math.add 10 20 # $ = 30
clear # $ = null
# Clear after chained operations
math.add 5 5
math.multiply $ 2 # $ = 20
clear # $ = null
# Clear doesn't affect variables
$testVar = 42
math.add 10 20
clear
log $testVar # Still prints 42
log $ # Prints null
The clear command sets the last return value ($) to null. It does not affect variables or any other state.
forget - Hide a variable or function in the current scope:
# Forget a variable in current scope
$testVar = 100
scope
forget $testVar
log $testVar # Prints null (variable is hidden)
endscope
log $testVar # Prints 100 (variable accessible again after scope)
# Forget a function in current scope
def my_function
return 42
enddef
scope
forget my_function
my_function # Error: Function is forgotten in current scope
endscope
my_function # Works normally (function accessible after scope)
# Forget a built-in command in current scope
scope
forget log
log "test" # Error: Built-in command is forgotten in current scope
endscope
log "test" # Works normally (built-in accessible after scope)
# Forget only affects the current scope
$outerVar = 200
scope
forget $outerVar
scope
log $outerVar # Prints 200 (accessible in child scope)
endscope
log $outerVar # Prints null (forgotten in current scope)
endscope
log $outerVar # Prints 200 (accessible again after scope)
The forget command hides a variable or function only in the current scope:
nullforget was calledgetType - Get the type of a variable:
# Get type of different variables
$str = "hello"
getType $str # Returns "string"
$num = 42
getType $num # Returns "number"
$bool = true
getType $bool # Returns "boolean"
$nullVar = null
getType $nullVar # Returns "null"
$arr = range 1 5
getType $arr # Returns "array"
obj '{name: "John"}'
$obj = $
getType $obj # Returns "object"
The getType command returns the type of a variable as a string. Possible return values:
"string" - String values"number" - Numeric values"boolean" - Boolean values (true/false)"null" - Null values"array" - Array values"object" - Object values (including empty objects)"undefined" - Undefined values (rare in RobinPath)Isolated Scopes with Parameters: Scopes can be declared with parameters to create isolated execution contexts that don't inherit from parent scopes:
# Regular scope (inherits from parent)
$parentVar = 100
scope
log $parentVar # Prints 100 (can access parent)
$localVar = 200
endscope
# Isolated scope with parameters (no parent access)
$outerVar = 300
scope $a $b
log $outerVar # Prints null (cannot access parent)
log $a # Prints null (parameter, defaults to null)
log $b # Prints null (parameter, defaults to null)
$localVar = 400
log $localVar # Prints 400 (local variable works)
endscope
log $outerVar # Prints 300 (unchanged)
# Isolated scope with multiple parameters
scope $x $y $z
log $x # Prints null (first parameter)
log $y # Prints null (second parameter)
log $z # Prints null (third parameter)
$local = 500
endscope
# Nested isolated scopes
scope $x
log $x # Prints null (parameter)
scope $y
log $x # Prints null (cannot access outer scope parameter)
log $y # Prints null (own parameter)
endscope
endscope
When a scope is declared with parameters:
null if not providedLines starting with # are comments:
# This is a comment
add 1 2 # Inline comment
Inline if:
if $age >= 18 then log "Adult"
Block if:
if $score >= 90
log "Grade: A"
elseif $score >= 80
log "Grade: B"
else
log "Grade: F"
endif
For loops:
for $i in range 1 5
log "Iteration:" $i
endfor
For loop with array:
$numbers = range 10 12
for $num in $numbers
log "Number:" $num
endfor
Break statement:
Use break to exit a for loop early:
for $i in range 1 10
if $i == 5
break # Exits the loop when $i equals 5
endif
log "Iteration:" $i
endfor
# This will only log iterations 1-4
The break statement:
for loop (will throw an error if used outside)$) from the iteration where break was executedExample with nested loops:
for $i in range 1 3
log "Outer:" $i
for $j in range 1 5
if $j == 3
break # Only breaks the inner loop
endif
log "Inner:" $j
endfor
endfor
# Outer loop continues, inner loop breaks at $j == 3
Define custom functions:
def greet
$1
$2
log "Hello" $1
log "Your age is" $2
add $2 1
enddef
greet "Alice" 25
log "Next year:" $ # Prints 26
Function Definition with Named Parameters: You can define functions with named parameter aliases:
def greet $name $age
log "Hello" $name
log "Your age is" $age
# $name is an alias for $1, $age is an alias for $2
# You can still use $1, $2, etc.
enddef
greet "Alice" 25
Function Call Syntax: RobinPath supports two ways to call functions:
CLI-style (existing):
greet "Alice" 25
math.add 10 20 key1=value1 key2=value2
Parenthesized style (new, recommended for complex calls):
greet("Alice" 25)
math.add(10 20 key1=value1 key2=value2)
# Multi-line parenthesized calls (recommended for longer calls)
greet(
"Alice"
25
)
math.add(
10
20
key1=value1
key2=value2
)
Both forms are equivalent. The parenthesized form is optimized for readability, multiline usage, and IDE tooling.
Named Arguments:
Functions can accept named arguments using key=value syntax:
def process $data
log "Data:" $data
log "Retries:" $args.retries
log "Timeout:" $args.timeout
log "Verbose:" $args.verbose
enddef
# CLI-style with named arguments
process $myData retries=3 timeout=30 verbose=true
# Parenthesized style with named arguments
process($myData retries=3 timeout=30 verbose=true)
# Multi-line parenthesized call
process(
$myData
retries=3
timeout=30
verbose=true
)
Accessing Arguments Inside Functions: Inside a function, you can access arguments in three ways:
$1, $2, $3, etc.$name, $age, etc. (if defined in function signature)$args.keyName for named argumentsdef example $a $b $c
# Positional arguments
log "First:" $1 # Same as $a
log "Second:" $2 # Same as $b
log "Third:" $3 # Same as $c
# Named parameter aliases
log "a:" $a # Same as $1
log "b:" $b # Same as $2
log "c:" $c # Same as $3
# Named arguments (from key=value in function call)
log "key:" $args.key
log "flag:" $args.flag
enddef
example("x" "y" "z" key=1 flag=true)
Parameter Binding Rules:
$a, $b, $c are aliases for $1, $2, $3 respectively$4, $5, etc.null$args.<name>$args.<name> refers to the named argumentFunctions can return values in two ways:
Implicit return (last value): Functions automatically return the last computed value:
def sum_and_double
add $1 $2
multiply $ 2
enddef
sum_and_double 10 20
log $ # Prints 60
Explicit return statement:
Use the return statement to return a value and terminate function execution:
def calculate
if $1 > 10
return 100
endif
multiply $1 2
enddef
calculate 5
log $ # Prints 10
calculate 15
log $ # Prints 100 (returned early)
The return statement can return:
return 42 or return "hello"return $result$): return (no value specified)return $(add 5 5)Return in global scope:
The return statement also works in global scope to terminate script execution:
log "This will execute"
return "done"
log "This will not execute"
Use modules to access specialized functions:
use math
math.add 5 10
use string
string.length "hello"
string.toUpperCase "world"
Available Modules:
math - Mathematical operations (add, subtract, multiply, divide, etc.)string - String manipulation (length, substring, replace, etc.)json - JSON parsing and stringification (parse, stringify, isValid)object - Object manipulation operations (get, set, keys, values, entries, merge, clone) - Global moduletime - Date and time operationsrandom - Random number generationarray - Array operations (push, pop, slice, etc.)Object Module (Global):
The Object module provides object manipulation functions and is available globally (no use command needed):
# Create objects using obj command (JSON5 syntax)
obj '{name: "John", age: 30}'
$user = $
# Or use json.parse for standard JSON
json.parse '{"name": "John", "age": 30}'
$user2 = $
# Get a value using dot-notation path
get $user "name" # Returns "John"
get $user "age" # Returns 30
# Alternative: Use attribute access syntax (see Attribute Access section)
# $user.name # Also returns "John"
# $user.age # Also returns 30
# Set a value using dot-notation path
set $user "city" "NYC" # Sets user.city = "NYC"
get $user "city" # Returns "NYC"
# Get object keys, values, and entries
keys $user # Returns ["name", "age", "city"]
values $user # Returns ["John", 30, "NYC"]
entries $user # Returns [["name", "John"], ["age", 30], ["city", "NYC"]]
# Merge objects
obj '{a: 1}'
$obj1 = $
obj '{b: 2}'
$obj2 = $
merge $obj1 $obj2 # Returns {a: 1, b: 2}
# Clone an object (deep copy)
clone $user # Returns a deep copy of $user
Use $( ... ) for inline subexpressions to evaluate code and use its result:
# Single-line subexpression
log "Result:" $(add 10 20)
# Multi-line subexpression
$result = $(
add 5 5
multiply $ 2
)
log $result # Prints 20
# Nested subexpressions
$value = $(add $(multiply 2 3) $(add 1 1))
log $value # Prints 8
# Subexpression in conditionals
if $(math.add 5 5) == 10
log "Equal to 10"
endif
# Function calls in subexpressions use parenthesized syntax
if $(test.isBigger $value 5)
log "Value is bigger than 5"
endif
Variable Scope in Subexpressions:
Subexpressions can read and modify variables from parent scopes. If a variable exists in a parent scope, the assign command will modify that variable:
$testVar = 10
$result = $(
assign $testVar 42 # This modifies the parent $testVar
math.add $testVar 8 # Uses 42, returns 50
)
log $result # Prints 50
log $testVar # Prints 42 (modified by subexpression)
If a variable doesn't exist in parent scopes, it will be created in the global scope:
$result = $(
assign $newVar 100 # Creates $newVar in global scope
math.add $newVar 1
)
log $result # Prints 101
log $newVar # Prints 100 (created in global scope)
Note: Functions (def/enddef) maintain their own local scope and do not modify parent variables.
Strings can use single quotes, double quotes, or backticks:
$msg1 = "Hello"
$msg2 = 'World'
$msg3 = `Template`
Automatic String Concatenation: Adjacent string literals are automatically concatenated:
$long = "hello " "world " "from RobinPath"
log $long # Prints "hello world from RobinPath"
This is particularly useful with backslash line continuation (see below).
Numbers can be integers or decimals:
$int = 42
$float = 3.14
The backslash (\) allows a single logical command to be written across multiple physical lines. If a line's last non-whitespace character is a backslash, that line is continued onto the next line.
Basic Usage:
log "this is a very long message " \
"that continues on the next line"
Multiple Continuations:
do_something $a $b $c \
$d $e $f \
$g $h $i
With String Concatenation:
$long = "hello " \
"world " \
"from RobinPath"
# Becomes: $long = "hello " "world " "from RobinPath"
# Which is automatically concatenated to: "hello world from RobinPath"
With Function Calls:
fn($a $b $c \
key1=1 \
key2=2)
With If Conditions:
if $a > 0 && \
$b < 10 && \
$c == "ok"
log "conditions met"
endif
With Assignments:
$query = "SELECT * FROM users " \
"WHERE active = 1 " \
"ORDER BY created_at DESC"
Rules:
\You can extend RobinPath by creating your own custom modules. Modules provide a way to organize related functions and make them available through the use command.
A module consists of three main parts:
Create a new TypeScript file in src/modules/ directory, for example src/modules/MyModule.ts:
import type {
BuiltinHandler,
FunctionMetadata,
ModuleMetadata,
ModuleAdapter
} from '../index';
/**
* MyModule for RobinPath
* Provides custom functionality
*/
// 1. Define your functions
export const MyModuleFunctions: Record<string, BuiltinHandler> = {
greet: (args) => {
const name = String(args[0] ?? 'World');
return `Hello, ${name}!`;
},
double: (args) => {
const num = Number(args[0]) || 0;
return num * 2;
},
// Functions can be async
delay: async (args) => {
const ms = Number(args[0]) || 1000;
await new Promise(resolve => setTimeout(resolve, ms));
return `Waited ${ms}ms`;
}
};
// 2. Define function metadata (for documentation and type checking)
export const MyModuleFunctionMetadata: Record<string, FunctionMetadata> = {
greet: {
description: 'Greets a person by name',
parameters: [
{
name: 'name',
dataType: 'string',
description: 'Name of the person to greet',
formInputType: 'text',
required: false,
defaultValue: 'World'
}
],
returnType: 'string',
returnDescription: 'Greeting message',
example: 'mymodule.greet "Alice" # Returns "Hello, Alice!"'
},
double: {
description: 'Doubles a number',
parameters: [
{
name: 'value',
dataType: 'number',
description: 'Number to double',
formInputType: 'number',
required: true
}
],
returnType: 'number',
returnDescription: 'The input number multiplied by 2',
example: 'mymodule.double 5 # Returns 10'
},
delay: {
description: 'Waits for a specified number of milliseconds',
parameters: [
{
name: 'ms',
dataType: 'number',
description: 'Number of milliseconds to wait',
formInputType: 'number',
required: true
}
],
returnType: 'string',
returnDescription: 'Confirmation message',
example: 'mymodule.delay 1000 # Waits 1 second'
}
};
// 3. Define module metadata
export const MyModuleModuleMetadata: ModuleMetadata = {
description: 'Custom module providing greeting and utility functions',
methods: [
'greet',
'double',
'delay'
]
};
// 4. Create and export the module adapter
const MyModule: ModuleAdapter = {
name: 'mymodule',
functions: MyModuleFunctions,
functionMetadata: MyModuleFunctionMetadata,
moduleMetadata: MyModuleModuleMetadata
};
export default MyModule;
In src/index.ts, import your module and add it to the NATIVE_MODULES array:
// Add import at the top with other module imports
import MyModule from './modules/MyModule';
// Add to NATIVE_MODULES array (around line 2504)
private static readonly NATIVE_MODULES: ModuleAdapter[] = [
MathModule,
StringModule,
JsonModule,
TimeModule,
RandomModule,
ArrayModule,
TestModule,
MyModule // Add your module here
];
Once registered, you can use your module in RobinPath scripts:
use mymodule
mymodule.greet "Alice"
mymodule.double 7
mymodule.delay 500
Function Signature: Functions must match the BuiltinHandler type:
(args: Value[]) => Value | Promise<Value>
Argument Handling: Always handle missing or undefined arguments:
const value = args[0] ?? defaultValue;
const num = Number(args[0]) || 0; // For numbers
const str = String(args[0] ?? ''); // For strings
Error Handling: Throw descriptive errors:
if (num < 0) {
throw new Error('Number must be non-negative');
}
Async Functions: Functions can return Promise<Value> for async operations:
asyncFunction: async (args) => {
await someAsyncOperation();
return result;
}
Parameter Metadata: Each parameter should include:
name: Parameter namedataType: One of 'string' | 'number' | 'boolean' | 'object' | 'array' | 'null' | 'any'description: Human-readable descriptionformInputType: UI input type (see FormInputType in code)required: Whether parameter is required (defaults to true)defaultValue: Optional default valueFunction Metadata: Each function should include:
description: What the function doesparameters: Array of parameter metadatareturnType: Return data typereturnDescription: What the function returnsexample: Optional usage exampleModule Metadata: Should include:
description: Overall module descriptionmethods: Array of all function names in the moduleHere's a complete example of a utility module:
import type {
BuiltinHandler,
FunctionMetadata,
ModuleMetadata,
ModuleAdapter
} from '../index';
export const UtilFunctions: Record<string, BuiltinHandler> = {
reverse: (args) => {
const str = String(args[0] ?? '');
return str.split('').reverse().join('');
},
capitalize: (args) => {
const str = String(args[0] ?? '');
if (str.length === 0) return str;
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
},
isEmpty: (args) => {
const value = args[0];
if (value === null || value === undefined) return true;
if (typeof value === 'string') return value.length === 0;
if (Array.isArray(value)) return value.length === 0;
if (typeof value === 'object') return Object.keys(value).length === 0;
return false;
}
};
export const UtilFunctionMetadata: Record<string, FunctionMetadata> = {
reverse: {
description: 'Reverses a string',
parameters: [
{
name: 'str',
dataType: 'string',
description: 'String to reverse',
formInputType: 'text',
required: true
}
],
returnType: 'string',
returnDescription: 'Reversed string',
example: 'util.reverse "hello" # Returns "olleh"'
},
// ... other function metadata
};
export const UtilModuleMetadata: ModuleMetadata = {
description: 'Utility functions for common operations',
methods: ['reverse', 'capitalize', 'isEmpty']
};
const UtilModule: ModuleAdapter = {
name: 'util',
functions: UtilFunctions,
functionMetadata: UtilFunctionMetadata,
moduleMetadata: UtilModuleMetadata
};
export default UtilModule;
mymodule, util, custom)After creating your module, test it in the REPL:
npm run cli
Then try:
use mymodule
mymodule.greet "Test"
You can also check available modules:
module list
add 10 20
$result
log "Sum:" $result
multiply $result 2
log "Double:" $
# Direct assignment
$name = "Alice"
$age = 25
# Variable-to-variable assignment
$name2 = $name
$age2 = $age
# Chained assignments
$original = 100
$copy1 = $original
$copy2 = $copy1
log $name2 $age2 # Prints "Alice" 25
log $copy2 # Prints 100
assign command:
# Basic assignment
assign $result "success"
assign $count 42
# Assignment with fallback
$maybeEmpty = null
assign $result $maybeEmpty "default" # $result = "default"
$maybeEmpty = ""
assign $name $maybeEmpty "Unknown" # $name = "Unknown"
$hasValue = "Alice"
assign $name $hasValue "Unknown" # $name = "Alice" (fallback not used)
empty command:
$data = "some data"
empty $data
log $data # Prints null
$arr = range 1 5
empty $arr
log $arr # Prints null
fallback command:
# Use fallback when variable might be empty
$name = null
$displayName = fallback $name "Guest"
log $displayName # Prints "Guest"
$name = "Alice"
$displayName = fallback $name "Guest"
log $displayName # Prints "Alice"
# Chain with other operations
$count = null
add fallback $count 0 10 # Adds 0 + 10 = 10
$age = 18
$citizen = "yes"
if ($age >= 18) && ($citizen == "yes") then log "Loan approved"
$arr = range 1 5
for $num in $arr
log "Number:" $num
endfor
# Create objects using obj command (JSON5 syntax - more flexible)
obj '{name: "John", age: 30, address: {city: "NYC"}, scores: [85, 90, 95]}'
$user = $
# Or use json.parse for standard JSON
json.parse '{"name": "John", "age": 30, "address": {"city": "NYC"}, "scores": [85, 90, 95]}'
$user2 = $
# Access properties using dot notation
log "Name:" $user.name
log "Age:" $user.age
log "City:" $user.address.city
# Access array elements
log "First score:" $user.scores[0]
log "Last score:" $user.scores[2]
# Use in conditionals
if $user.age >= 18
log "Adult user"
endif
# Use in calculations
math.add $user.scores[0] $user.scores[1]
log "Sum of first two scores:" $
# Create objects with obj command (JSON5 features)
obj '{unquoted: "keys work", trailing: "comma", allowed: true,}'
$config = $
log $config.unquoted # Prints "keys work"
Implicit return:
def calculate
multiply $1 $2
add $ 10
enddef
calculate 5 3
log "Result:" $ # Prints 25
Explicit return:
def calculate
if $1 > 10
return 100
endif
multiply $1 $2
add $ 10
enddef
calculate 15 3
log "Result:" $ # Prints 100 (returned early)
calculate 5 3
log "Result:" $ # Prints 25
Run the test suite:
npm test
This will execute the test script located in test/test.rp.
Build the project:
npm run build
FAQs
RobinPath - A lightweight, fast, and easy-to-use scripting language for automation and data processing.
The npm package @wiredwp/robinpath receives a total of 10 weekly downloads. As such, @wiredwp/robinpath popularity was classified as not popular.
We found that @wiredwp/robinpath demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
npm v12 is generally available, turning install scripts off by default and beginning the deprecation of 2FA-bypass publishing tokens.

Research
/Security News
Socket tracks the activity as Operation “Muck and Load”: a threat actor uses commit-farming workflows, public dead drops, and protected archives to stage Windows RAT and infostealer malware.

Security News
pnpm 11.10 hardens registry auth to block token redirection, tightens pack-app and deploy, and makes the Rust port (v12) installable.