npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@forgehive/record-tape

v0.2.7

Published

A lightweight TypeScript library for recording and persisting task execution logs with boundary data support. Works generically with any task type without requiring compile-time type knowledge.

Downloads

27

Readme

@forgehive/record-tape

A lightweight TypeScript library for recording and persisting task execution logs with boundary data support. Works generically with any task type without requiring compile-time type knowledge.

Features

  • 🎯 Simple API: Clean, minimal interface for recording execution data
  • 📝 Automatic Logging: Seamless integration with task execution
  • 💾 Persistence: Save and load execution logs to/from files
  • 🔄 Serialization: Built-in JSON serialization support
  • 🎨 TypeScript: Full type safety with generics
  • 🚀 Boundary Support: Record external API calls and dependencies
  • 🔧 Generic Design: Works with any task type without compile-time type requirements

Installation

pnpm add @forgehive/record-tape

Quick Start

import { RecordTape } from '@forgehive/record-tape'
import { createTask, Schema } from '@forgehive/task'

// Create a new recording tape
const tape = new RecordTape({ path: 'logs/execution' })

// Create a simple task
const getUserTask = createTask({
  name: 'getUserById',
  schema: Schema.object({
    userId: Schema.number()
  }),
  boundaries: {},
  fn: async function getUserById({ userId }) {
    // Simulate getting user data
    return { name: 'John Doe', email: '[email protected]' }
  }
})

// Execute the task - safeRun returns result, error, and execution record
const [result, error, executionRecord] = await getUserTask.safeRun({ userId: 123 })

// Manually record the execution to the tape
tape.push(executionRecord)

// Save recorded data to file
await tape.save()

// Optional: View what was recorded
console.log('Recorded logs:', tape.getLog())

API Reference

Constructor

new RecordTape<TInput, TOutput, B>(config?: Config)

Parameters:

  • config.path? - File path for persistence (will append .log extension)
  • config.log? - Pre-existing log records to initialize with
  • config.boundaries? - Initial boundary data

Core Methods

getLog()

Get all recorded execution logs.

const logs = tape.getLog()
// Returns: GenericExecutionRecord<TInput, TOutput, B>[]

getLength()

Get the number of records in the tape.

const count = tape.getLength()
// Returns: number

shift()

Remove and return the first record from the tape.

const firstRecord = tape.shift()
// Returns: GenericExecutionRecord<TInput, TOutput, B> | undefined

push(record, metadata?)

Add a new execution record to the tape.

const logRecord = tape.push(executionRecord, { userId: '123' })

Parameters:

  • record - ExecutionRecord with input, output, taskName, boundaries, etc.
  • metadata? - Optional additional metadata to attach

Returns: The created GenericExecutionRecord

recordFrom(task)

Set up automatic recording from a task instance.

tape.recordFrom(myTask)
// Now myTask will automatically send execution records to this tape

Serialization Methods

stringify()

Convert all logs to a string format (one JSON object per line).

const logString = tape.stringify()

parse(content)

Parse string content back into log records.

const logs = tape.parse(logString)
// Returns: GenericExecutionRecord<TInput, TOutput, B>[]

Persistence Methods

load() / loadSync()

Load execution logs from file.

// Async version
const logs = await tape.load()
// Returns: GenericExecutionRecord<TInput, TOutput, B>[]

// Sync version
const logs = tape.loadSync()
// Returns: GenericExecutionRecord<TInput, TOutput, B>[]

save() / saveSync()

Save execution logs to file.

// Async version
await tape.save()

// Sync version
tape.saveSync()

Utility Methods

compileCache()

Compile boundary data from all logs into a cache structure.

const boundaryCache = tape.compileCache()
// Returns: Record<string, unknown>

Types

GenericExecutionRecord

A generic version of ExecutionRecord that can store execution data from any task without knowing the specific types at compile time:

interface GenericExecutionRecord<TInput = unknown, TOutput = unknown, B extends Boundaries = Boundaries> extends ExecutionRecord<TInput, TOutput, B> {
}

The "Generic" prefix indicates that RecordTape can store logs from different tasks with varying input/output types. The default type parameters (unknown) allow the tape to work with any task execution without requiring specific type knowledge.

Why Generic?

  • Mixed Task Logs: Store logs from multiple different tasks in the same tape
  • Runtime Flexibility: Add logs without knowing task types at compile time
  • Type Safety: Still maintains TypeScript type safety when types are known

ExecutionRecord

Core execution data structure:

interface ExecutionRecord<TInput, TOutput, B extends Boundaries> {
  input: TInput
  output?: TOutput | null
  error?: string
  boundaries: BoundaryLogsFor<B>
  taskName?: string
  metadata?: Record<string, string>
  type: 'success' | 'error' | 'pending'
}

Usage Examples

Basic Recording

import { RecordTape } from '@forgehive/record-tape'

const tape = new RecordTape({ path: 'logs/user-service' })

// Record a successful execution
tape.push({
  input: { userId: 123 },
  output: { name: 'John Doe', email: '[email protected]' },
  taskName: 'getUser',
  boundaries: {},
  type: 'success'
})

// Record an error
tape.push({
  input: { userId: 999 },
  error: 'User not found',
  taskName: 'getUser',
  boundaries: {},
  type: 'error'
})

await tape.save()

Queue-like Processing

import { RecordTape } from '@forgehive/record-tape'

const tape = new RecordTape({ path: 'logs/processing-queue' })

// Add several records
tape.push({
  input: { task: 'process-order-1' },
  output: { status: 'completed' },
  taskName: 'processOrder',
  boundaries: {},
  type: 'success'
})

tape.push({
  input: { task: 'process-order-2' },
  output: { status: 'completed' },
  taskName: 'processOrder',
  boundaries: {},
  type: 'success'
})

// Check how many records we have
console.log(`Total records: ${tape.getLength()}`) // 2

// Process records in FIFO order
while (tape.getLength() > 0) {
  const record = tape.shift()
  console.log(`Processing: ${record?.input.task}`)
}

console.log(`Remaining records: ${tape.getLength()}`) // 0

With Boundary Data

const tape = new RecordTape({ path: 'logs/api-calls' })

tape.push({
  input: { query: 'nodejs' },
  output: { results: ['result1', 'result2'] },
  taskName: 'searchRepositories',
  boundaries: {
    githubAPI: [
      {
        input: ['/search/repositories?q=nodejs'],
        output: { data: ['repo1', 'repo2'] },
        error: null
      }
    ]
  },
  type: 'success'
})

Task Integration

import { Task } from '@forgehive/task'
import { RecordTape } from '@forgehive/record-tape'

const tape = new RecordTape({ path: 'logs/my-task' })
const myTask = new Task(myFunction, { name: 'processData' })

// Set up automatic recording
tape.recordFrom(myTask)

// Now all task executions will be recorded
await myTask.safeRun({ data: 'test' })

// Save recorded data
await tape.save()

Loading and Analyzing Logs

const tape = new RecordTape({ path: 'logs/analysis' })

// Load existing logs
const logs = await tape.load()

// Analyze execution patterns
const successCount = logs.filter(log => log.type === 'success').length
const errorCount = logs.filter(log => log.type === 'error').length

console.log(`Success rate: ${(successCount / logs.length * 100).toFixed(1)}%`)

// Compile boundary cache for replay
const boundaryCache = tape.compileCache()

File Format

The tape saves logs in a simple text format with one JSON object per line:

{"input":{"userId":123},"output":{"name":"John"},"type":"success","boundaries":{},"metadata":{},"taskName":"getUser"}
{"input":{"userId":999},"error":"User not found","type":"error","boundaries":{},"metadata":{},"taskName":"getUser"}

Advanced Usage

Custom Metadata

tape.push(executionRecord, {
  requestId: 'req-123',
  userAgent: 'MyApp/1.0',
  timestamp: new Date().toISOString()
})

Boundary Cache Compilation

// After recording multiple executions with boundary data
const cache = tape.compileCache()

// Use cache for task replay
const replayTask = new Task(myFunction, {
  boundariesData: cache
})

Migration Guide

Recent API Changes

Interface Rename: LogRecordGenericExecutionRecord

  • The main interface has been renamed to better reflect its generic nature
  • All method signatures now use GenericExecutionRecord
  • The "Generic" prefix indicates the tape can store logs from different task types

Simplified API:

  • Removed deprecated setMode(), getMode(), addLogRecord(), addExecutionRecord() methods
  • Simplified push() method with automatic type inference
  • Removed redundant name field (use taskName instead)

New Queue Methods:

  • Added getLength() - returns number of records
  • Added shift() - removes and returns first record (FIFO)
  • These enable efficient log management and processing

Task Integration:

  • Direct ExecutionRecord compatibility with Task package
  • Simplified listener setup with automatic record forwarding
  • No conversion needed between task records and tape records

License

MIT License - see LICENSE file for details.