# JavaScript Interop This document covers Baba Yaga's JavaScript interoperability features, which allow safe and controlled access to JavaScript functionality while maintaining Baba Yaga's functional programming guarantees. ## Table of Contents 1. [Overview](#overview) 2. [Core Functions](#core-functions) 3. [Type Conversion](#type-conversion) 4. [Security Model](#security-model) 5. [Common Patterns](#common-patterns) 6. [Error Handling](#error-handling) 7. [Configuration](#configuration) 8. [Best Practices](#best-practices) 9. [Examples](#examples) ## Overview Baba Yaga's JavaScript interop system provides a safe bridge between Baba Yaga's functional, immutable world and JavaScript's imperative, mutable one. All JavaScript operations return `Result` types to maintain explicit error handling. ### Key Principles - **Safety First**: All JS operations are sandboxed and return `Result` types - **Explicit Boundaries**: Clear separation between Baba Yaga and JavaScript - **Type Safety**: Automatic conversion between type systems - **Error Isolation**: JavaScript errors become Baba Yaga `Err` values ## Core Functions All JavaScript interop functions are available in the `io.*` namespace. ### Function Calls #### `io.callJS` Call a JavaScript function synchronously. ```baba io.callJS : (functionName: String, args: [Any]) -> Result // Examples absResult : io.callJS "Math.abs" [-42]; // Returns: Ok (JSValue 42) parseResult : io.callJS "JSON.parse" ["{\"x\": 10}"]; // Returns: Ok (JSValue {x: 10}) // Note: io.callJS returns a Result whose Ok value is a JSValue wrapper // around the raw JavaScript value. You can pass this JSValue directly to // io.getProperty, io.setProperty, io.hasProperty, io.jsArrayToList, // io.objectToTable, etc. without manual unwrapping. ``` #### `io.callJSAsync` Call a JavaScript function asynchronously (if async operations are enabled). ```baba io.callJSAsync : (functionName: String, args: [Any]) -> Result // Example (requires enableAsyncOps: true) fetchResult : io.callJSAsync "fetch" ["https://api.example.com/data"]; ``` ### Property Access #### `io.getProperty` Get a property from a JavaScript object. ```baba io.getProperty : (obj: Any, propName: String) -> Result // Example obj : io.callJS "JSON.parse" ["{\"name\": \"Alice\"}"]; nameResult : when obj is Ok parsed then io.getProperty parsed "name" Err msg then Err msg; // Returns: Ok "Alice" (direct Baba Yaga string) ``` #### `io.setProperty` Set a property on a JavaScript object (mutates the object). ```baba io.setProperty : (obj: Any, propName: String, value: Any) -> Result // Example obj : io.callJS "JSON.parse" ["{}"]; result : when obj is Ok parsed then io.setProperty parsed "newProp" 42 Err msg then Err msg; ``` #### `io.hasProperty` Check if a property exists on a JavaScript object. ```baba io.hasProperty : (obj: Any, propName: String) -> Bool // Example obj : io.callJS "JSON.parse" ["{\"x\": 10}"]; hasX : when obj is Ok parsed then io.hasProperty parsed "x" Err _ then false; // Returns: true ``` ### Type Conversion #### `io.jsArrayToList` Convert a JavaScript array to a Baba Yaga list. ```baba io.jsArrayToList : (jsArray: Any) -> Result // Example jsArray : io.callJS "JSON.parse" ["[1, 2, 3]"]; listResult : when jsArray is Ok arr then io.jsArrayToList arr Err msg then Err msg; // Returns: Ok [1, 2, 3] (direct Baba Yaga list) ``` #### `io.listToJSArray` Convert a Baba Yaga list to a JavaScript array. ```baba io.listToJSArray : (list: [Any]) -> Any // Example babaList : [1, 2, 3, 4, 5]; jsArray : io.listToJSArray babaList; jsonResult : io.callJS "JSON.stringify" [jsArray]; // Returns: Ok (JSValue "[1,2,3,4,5]") ``` #### `io.objectToTable` Convert a JavaScript object to a Baba Yaga table. ```baba io.objectToTable : (obj: Any) -> Result // Example jsObj : io.callJS "JSON.parse" ["{\"name\": \"Bob\", \"age\": 25}"]; tableResult : when jsObj is Ok obj then io.objectToTable obj Err msg then Err msg; // Returns: Ok {name: "Bob", age: 25} (direct Baba Yaga table) ``` #### `io.tableToObject` Convert a Baba Yaga table to a JavaScript object. ```baba io.tableToObject : (table: Table) -> Any // Example babaTable : {x: 100, y: 200}; jsObj : io.tableToObject babaTable; jsonResult : io.callJS "JSON.stringify" [jsObj]; // Returns: Ok (JSValue "{\"x\":100,\"y\":200}") ``` ### Error Management #### `io.getLastJSError` Get the last JavaScript error that occurred. Note: depending on language syntax rules for zero-argument functions, direct invocation may not be available in all contexts. Prefer handling errors from `io.callJS` directly via the returned `Result`. #### `io.clearJSError` Clear the last JavaScript error. Note: same invocation caveat as above applies. ## Type Conversion ### Automatic Conversions The JavaScript bridge automatically converts between Baba Yaga and JavaScript types: | Baba Yaga Type | JavaScript Type | Notes | |----------------|-----------------|-------------------------------------| | `Number` | `number` | Preserves integer/float distinction | | `String` | `string` | Direct conversion | | `Bool` | `boolean` | Direct conversion | | `List` | `Array` | Recursive conversion of elements | | `Table` | `Object` | Converts Map to plain object | | `Result` | N/A | Handled at boundary | ### Manual Conversions For more control, use explicit conversion functions: ```baba // Safe JSON parsing with error handling parseJSON : jsonString -> when (validate.type "String" jsonString) is false then Err "Input must be a string" true then when (io.callJS "JSON.parse" [jsonString]) is Ok parsed then Ok (io.objectToTable parsed) Err msg then Err ("JSON parse error: " .. msg); // Usage result : parseJSON "{\"user\": \"Alice\", \"score\": 95}"; ``` ## Security Model The JavaScript interop system uses a configurable security model: ### Sandboxed Execution All JavaScript code runs in a controlled sandbox with: - **Limited Global Access**: Only allowed globals are available - **Function Whitelist**: Only explicitly allowed functions can be called - **Timeout Protection**: Operations have configurable time limits - **Memory Limits**: Configurable memory usage constraints ### Default Allowed Functions By default, these JavaScript functions are available: ```javascript // JSON operations 'JSON.parse', 'JSON.stringify', // Math operations 'Math.abs', 'Math.floor', 'Math.ceil', 'Math.round', 'Math.min', 'Math.max', 'Math.random', // Console operations 'console.log', 'console.warn', 'console.error', // Time operations 'Date.now', 'performance.now' ``` ### Configuration Configure the JavaScript bridge through the host configuration: ```javascript const host = { jsBridgeConfig: { allowedFunctions: new Set(['Math.abs', 'JSON.parse']), maxExecutionTime: 5000, // 5 seconds enableAsyncOps: false, // Disable async operations enableFileSystem: false, // Disable file system access enableNetwork: false // Disable network access } }; ``` ## Common Patterns ### Safe JSON Operations ```baba // Safe JSON parsing safeParseJSON : jsonStr -> when (io.callJS "JSON.parse" [jsonStr]) is Ok obj then when (io.objectToTable obj) is Ok table then Ok table Err msg then Err ("Conversion error: " .. msg) Err msg then Err ("Parse error: " .. msg); // Safe JSON stringification safeStringifyJSON : table -> jsObj : io.tableToObject table; io.callJS "JSON.stringify" [jsObj]; ``` ### Mathematical Operations ```baba // Safe mathematical operations with validation safeMath : operation args -> when (validate.notEmpty args) is false then Err "No arguments provided" true then when operation is "abs" then io.callJS "Math.abs" [head args] "min" then io.callJS "Math.min" args "max" then io.callJS "Math.max" args "round" then io.callJS "Math.round" [head args] _ then Err ("Unknown operation: " .. operation); // Usage result : safeMath "abs" [-42]; // Ok 42 minResult : safeMath "min" [10, 5, 8]; // Ok 5 ``` ### Working with JavaScript APIs ```baba // Date operations getCurrentTimestamp : () -> io.callJS "Date.now" []; formatDate : timestamp -> when (io.callJS "Date" [timestamp]) is Ok dateObj then io.callJS "Date.prototype.toISOString" [dateObj] Err msg then Err msg; // Performance monitoring measurePerformance : operation -> startTime : io.callJS "performance.now" []; result : operation; endTime : io.callJS "performance.now" []; duration : when (startTime, endTime) is (Ok start, Ok end) then Ok (end - start) _ then Err "Could not measure performance"; {result: result, duration: duration}; ``` ## Error Handling ### JavaScript Error Types JavaScript errors are automatically converted to Baba Yaga `Err` values: ```baba // This will return an Err result : io.callJS "JSON.parse" ["invalid json"]; // Returns: Err "Unexpected token i in JSON at position 0" // Handle different error types handleJSError : result -> when result is Ok value then processValue value Err msg then when (text.contains msg "JSON") is true then handleJSONError msg false then handleGenericError msg; ``` ### Error Recovery Patterns ```baba // Retry pattern retryOperation : operation maxAttempts -> attempt : 1; tryOperation : currentAttempt -> when (currentAttempt > maxAttempts) is true then Err "Max attempts exceeded" false then when (operation) is Ok result then Ok result Err _ then tryOperation (currentAttempt + 1); tryOperation attempt; // Fallback pattern withFallback : primaryOp fallbackOp -> when primaryOp is Ok result then Ok result Err _ then fallbackOp; ``` ## Best Practices ### 1. Always Use Result Types Never assume JavaScript operations will succeed: ```baba // Good result : when (io.callJS "Math.abs" [value]) is Ok abs then processValue abs Err msg then handleError msg; // Bad - assumes success abs : io.callJS "Math.abs" [value]; // This returns Result, not number ``` ### 2. Validate Inputs Always validate data before sending to JavaScript: ```baba // Good safeCall : value -> when (validate.type "Number" value) is false then Err "Value must be a number" true then io.callJS "Math.abs" [value]; // Bad - no validation unsafeCall : value -> io.callJS "Math.abs" [value]; ``` ### 3. Handle Type Conversions Explicitly Be explicit about type conversions: ```baba // Good processJSData : jsData -> when (io.objectToTable jsData) is Ok table then processTable table Err msg then Err ("Conversion failed: " .. msg); // Bad - assumes conversion works processJSData : jsData -> table : io.objectToTable jsData; processTable table; ``` ### 4. Use Composition for Complex Operations Break complex JavaScript interactions into smaller, composable functions: ```baba // Composed operations parseAndValidate : jsonStr schema -> parsed : safeParseJSON jsonStr; when parsed is Ok data then validateAgainstSchema data schema Err msg then Err msg; transformAndStringify : data transformer -> transformed : transformer data; safeStringifyJSON transformed; ``` ## Examples ### Complete JSON Processing Pipeline ```baba // Complete JSON processing with error handling processJSONData : jsonString -> // Parse JSON parseResult : io.callJS "JSON.parse" [jsonString]; when parseResult is Err msg then Err ("Parse failed: " .. msg) Ok jsObj then // Convert to Baba Yaga table when (io.objectToTable jsObj) is Err msg then Err ("Conversion failed: " .. msg) Ok table then // Process the data processedTable : processData table; // Convert back to JS object jsResult : io.tableToObject processedTable; // Stringify result io.callJS "JSON.stringify" [jsResult]; // Helper function processData : table -> // Add timestamp withTimestamp : table .. {timestamp: getCurrentTimestamp}; // Validate required fields when (hasRequiredFields withTimestamp) is false then table .. {error: "Missing required fields"} true then withTimestamp; // Usage input : "{\"name\": \"Alice\", \"score\": 95}"; result : processJSONData input; // Returns: Ok (JSValue "{\"name\":\"Alice\",\"score\":95,\"timestamp\":1640995200000}") ``` ### Working with JavaScript Arrays ```baba // Process JavaScript arrays processJSArray : jsArrayString -> // Parse array arrayResult : io.callJS "JSON.parse" [jsArrayString]; when arrayResult is Err msg then Err msg Ok jsArray then // Convert to Baba Yaga list when (io.jsArrayToList jsArray) is Err msg then Err msg Ok babaList then // Process with Baba Yaga functions processed : map (x -> x * 2) babaList; filtered : filter (x -> x > 10) processed; // Convert back to JS array jsResult : io.listToJSArray filtered; // Return as JSON io.callJS "JSON.stringify" [jsResult]; // Usage input : "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"; result : processJSArray input; // Returns: Ok (JSValue "[4,6,8,10,12,14,16,18,20]") ``` This JavaScript interop system provides a safe, controlled way to leverage JavaScript's ecosystem while maintaining Baba Yaga's functional programming principles and explicit error handling.