CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-stream-transform

Object transformations implementing the Node.js stream.Transform API

Pending
Overview
Eval results
Files

sync-api.mddocs/

Synchronous API

Synchronous data transformation for immediate processing of data arrays. The sync API provides blocking execution with direct return values, ideal for small datasets where simplicity and immediate results are prioritized.

Capabilities

Synchronous Transform Function

Transform an array of records synchronously with immediate results.

/**
 * Transform records synchronously
 * @param records - Array of data to transform
 * @param handler - Synchronous function to transform each record
 * @returns Array of transformed results
 */
function transform<T, U>(records: Array<T>, handler: Handler<T, U>): Array<U>;

/**
 * Transform with options synchronously
 * @param records - Array of data to transform
 * @param options - Configuration options (limited subset)
 * @param handler - Synchronous function to transform each record
 * @returns Array of transformed results
 */
function transform<T, U>(
  records: Array<T>, 
  options: Options, 
  handler: Handler<T, U>
): Array<U>;

/**
 * Synchronous handler type - must return result directly
 */
type Handler<T = any, U = any> = (record: T, params?: any) => U;

Module Import:

// Import from sync module
import { transform } from "stream-transform/sync";

// CommonJS
const { transform } = require("stream-transform/sync");

// Browser ESM
import { transform } from "stream-transform/browser/esm/sync";

Usage Examples:

import { transform } from "stream-transform/sync";

// Basic synchronous transformation
const numbers = [1, 2, 3, 4, 5];
const doubled = transform(numbers, (num) => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

// Object transformation
const users = [
  { name: " Alice ", age: "25" },
  { name: " Bob ", age: "30" }
];

const cleanUsers = transform(users, (user) => ({
  name: user.name.trim(),
  age: parseInt(user.age),
  processed: true
}));

console.log(cleanUsers);
// [
//   { name: "Alice", age: 25, processed: true },
//   { name: "Bob", age: 30, processed: true }
// ]

// Array manipulation
const records = [
  ["a", "b", "c"],
  ["1", "2", "3"],
  ["x", "y", "z"]
];

const joined = transform(records, (record) => record.join("|"));
console.log(joined); // ["a|b|c", "1|2|3", "x|y|z"]

Transform with Options

Enhanced synchronous transformation with configuration options.

/**
 * Transform with options synchronously
 * @param records - Array of data to transform
 * @param options - Configuration options
 * @param handler - Synchronous transformation function
 * @returns Array of transformed results
 */
function transform<T, U>(
  records: Array<T>, 
  options: Options, 
  handler: Handler<T, U>
): Array<U>;

interface Options {
  /** User-defined parameters passed to handler function */
  params?: any;
}

Usage Examples:

import { transform } from "stream-transform/sync";

// Using custom parameters
const data = [
  { value: 10, category: "A" },
  { value: 20, category: "B" },
  { value: 30, category: "A" }
];

const processed = transform(data, {
  params: { 
    multiplier: 2, 
    prefix: "item_",
    filter: "A"
  }
}, (record, params) => {
  if (record.category !== params.filter) {
    return null; // Skip non-matching records
  }
  return {
    id: params.prefix + record.value,
    value: record.value * params.multiplier,
    category: record.category
  };
});

// Filter out null results
const filtered = processed.filter(item => item !== null);
console.log(filtered);
// [
//   { id: "item_10", value: 20, category: "A" },
//   { id: "item_30", value: 60, category: "A" }
// ]

// Configuration-driven transformation
const config = {
  params: {
    dateFormat: "YYYY-MM-DD",
    timezone: "UTC",
    includeMetadata: true
  }
};

const events = [
  { timestamp: 1640995200000, event: "login" },
  { timestamp: 1640995260000, event: "logout" }
];

const formattedEvents = transform(events, config, (event, params) => {
  const date = new Date(event.timestamp);
  const formatted = {
    event: event.event,
    date: date.toISOString().split('T')[0], // Simple YYYY-MM-DD format
    time: date.toISOString().split('T')[1].split('.')[0] // HH:MM:SS format
  };
  
  if (params.includeMetadata) {
    formatted.metadata = {
      timezone: params.timezone,
      processed: new Date().toISOString()
    };
  }
  
  return formatted;
});

Handler Function Requirements

Synchronous API only supports synchronous handlers with specific signature requirements.

/**
 * Synchronous handler - returns result directly
 * @param record - Individual record to transform
 * @param params - Optional user-defined parameters
 * @returns Transformed result
 */
type Handler<T, U> = (record: T, params?: any) => U;

// Handler must be synchronous - these patterns are NOT supported:
// ❌ (record, callback) => void      // Async callback pattern
// ❌ (record) => Promise<result>     // Promise-based pattern  
// ❌ async (record) => result        // Async function pattern

Usage Examples:

import { transform } from "stream-transform/sync";

// ✅ Valid synchronous handlers
const result1 = transform([1, 2, 3], (num) => num * 2);

const result2 = transform(["a", "b"], (str) => str.toUpperCase());

const result3 = transform([{a: 1}, {a: 2}], (obj) => ({ ...obj, b: obj.a * 2 }));

// With parameters
const result4 = transform([1, 2, 3], { params: { factor: 10 } }, (num, params) => {
  return num * params.factor;
});

// ❌ Invalid - async patterns will throw errors
try {
  transform([1, 2, 3], (num, callback) => {
    // This will throw: "Invalid Handler: only synchonous handlers are supported"
    callback(null, num * 2);
  });
} catch (err) {
  console.error(err.message);
}

try {
  transform([1, 2, 3], async (num) => {
    // This will throw: "Invalid Handler: only synchonous handlers are supported"
    return num * 2;
  });
} catch (err) {
  console.error(err.message);
}

Record Manipulation Patterns

Various patterns for manipulating records in synchronous transformations.

// Record transformation patterns:
// - Return transformed record: normal transformation
// - Return null/undefined/"": skip record (will be filtered out)
// - Return array: multiply record (each array element becomes separate result)
// - Throw error: halt processing with error

Usage Examples:

import { transform } from "stream-transform/sync";

// Skip records by returning null/undefined/empty string
const numbers = [1, 2, 3, 4, 5, 6];
const evenOnly = transform(numbers, (num) => {
  if (num % 2 !== 0) {
    return null; // Skip odd numbers
  }
  return num * 2;
}).filter(result => result !== null); // Filter out nulls
console.log(evenOnly); // [4, 8, 12]

// Multiply records by returning arrays
const words = ["hello", "world"];
const letters = transform(words, (word) => {
  return word.split(''); // Each word becomes array of letters
}).flat(); // Flatten the results
console.log(letters); // ['h','e','l','l','o','w','o','r','l','d']

// Error handling with synchronous throws
const data = [1, 2, "invalid", 4];
try {
  const results = transform(data, (value) => {
    if (typeof value !== "number") {
      throw new Error(`Invalid value: ${value}`);
    }
    return value * 2;
  });
} catch (err) {
  console.error("Transformation failed:", err.message);
}

// Conditional transformation
const mixed = [1, "2", 3, "4", 5];
const processed = transform(mixed, (value) => {
  if (typeof value === "string") {
    return parseInt(value); // Convert strings to numbers
  }
  if (typeof value === "number") {
    return value * 2; // Double numbers
  }
  return null; // Skip other types
}).filter(result => result !== null);
console.log(processed); // [2, 2, 6, 4, 10]

Performance Characteristics

Understanding the performance profile of synchronous transformations.

// Performance characteristics:
// - Blocking execution: entire process waits for completion
// - Memory efficient: processes one record at a time
// - CPU intensive: no I/O concurrency benefits
// - Best for: Small datasets, CPU-bound operations, simple transformations
// - Avoid for: Large datasets, I/O operations, long-running processes

Usage Examples:

import { transform } from "stream-transform/sync";

// ✅ Good use cases for sync API
// Small datasets
const smallData = Array.from({ length: 100 }, (_, i) => i);
const processed = transform(smallData, (num) => num * num);

// Simple CPU operations
const texts = ["hello", "world", "sync", "api"];
const uppercased = transform(texts, (text) => text.toUpperCase());

// Data structure transformations
const objects = [{ a: 1, b: 2 }, { a: 3, b: 4 }];
const flattened = transform(objects, (obj) => [obj.a, obj.b]);

// ❌ Consider alternatives for these cases
// Large datasets - use stream API instead
const largeData = Array.from({ length: 1000000 }, (_, i) => i);
// const results = transform(largeData, processor); // May block for too long

// I/O operations - use async API instead  
const urls = ["http://api1.com", "http://api2.com"];
// const responses = transform(urls, (url) => {
//   return fetch(url); // Blocking I/O - not ideal
// });

// CPU-intensive operations - consider worker threads
const complexData = Array.from({ length: 10000 }, (_, i) => ({ data: i }));
// const computed = transform(complexData, (item) => {
//   return heavyComputation(item); // May block UI/other operations
// });

// Better alternatives for problematic cases:
import { transform as asyncTransform } from "stream-transform";

// For large datasets - use streaming
const streamProcessor = asyncTransform((record) => processRecord(record));

// For I/O operations - use async handlers
asyncTransform(urls, async (url) => {
  const response = await fetch(url);
  return await response.json();
}, (err, results) => {
  console.log("Async results:", results);
});

Error Handling

Synchronous error handling with immediate exception throwing.

// Errors are thrown synchronously and can be caught with try/catch
// Processing stops immediately when an error occurs
// No partial results are returned - either all succeed or all fail

Usage Examples:

import { transform } from "stream-transform/sync";

// Basic error handling
const data = [1, 2, 3, "invalid", 5];

try {
  const results = transform(data, (value) => {
    if (typeof value !== "number") {
      throw new Error(`Expected number, got ${typeof value}: ${value}`);
    }
    return value * 2;
  });
  console.log("All processed:", results);
} catch (err) {
  console.error("Processing failed:", err.message);
  // No partial results available
}

// Validation with detailed errors
const users = [
  { name: "Alice", email: "alice@example.com" },
  { name: "", email: "bob@example.com" }, // Invalid
  { name: "Charlie", email: "charlie@example.com" }
];

try {
  const validatedUsers = transform(users, (user, index) => {
    if (!user.name || user.name.trim() === "") {
      throw new Error(`User at index ${index} has invalid name`);
    }
    if (!user.email || !user.email.includes("@")) {
      throw new Error(`User at index ${index} has invalid email`);
    }
    return {
      ...user,
      name: user.name.trim(),
      email: user.email.toLowerCase(),
      validated: true
    };
  });
  console.log("All users validated:", validatedUsers);
} catch (err) {
  console.error("Validation error:", err.message);
}

// Graceful error handling with recovery
function safeTransform(data, handler) {
  const results = [];
  const errors = [];
  
  for (let i = 0; i < data.length; i++) {
    try {
      const result = handler(data[i], i);
      results.push(result);
    } catch (err) {
      errors.push({ index: i, error: err.message, input: data[i] });
    }
  }
  
  return { results, errors };
}

const mixed = [1, 2, "bad", 4, null, 6];
const { results, errors } = safeTransform(mixed, (value) => {
  if (typeof value !== "number") {
    throw new Error(`Invalid type: ${typeof value}`);
  }
  return value * 2;
});

console.log("Successful results:", results); // [2, 4, 8, 12]
console.log("Errors encountered:", errors);
// [
//   { index: 2, error: "Invalid type: string", input: "bad" },
//   { index: 4, error: "Invalid type: object", input: null }
// ]

Install with Tessl CLI

npx tessl i tessl/npm-stream-transform

docs

callback-api.md

index.md

stream-api.md

sync-api.md

tile.json