about summary refs log tree commit diff stats
path: root/js/baba-yaga/docs/09_js-interop.md
diff options
context:
space:
mode:
Diffstat (limited to 'js/baba-yaga/docs/09_js-interop.md')
-rw-r--r--js/baba-yaga/docs/09_js-interop.md500
1 files changed, 500 insertions, 0 deletions
diff --git a/js/baba-yaga/docs/09_js-interop.md b/js/baba-yaga/docs/09_js-interop.md
new file mode 100644
index 0000000..28ec7bb
--- /dev/null
+++ b/js/baba-yaga/docs/09_js-interop.md
@@ -0,0 +1,500 @@
+# 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.