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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@fimbul-works/fimbul

v1.3.1

Published

Manage complex computation dependencies with automatic topological sorting and built-in caching

Readme

Fimbul

Fimbul manages complex computation dependencies using directed acyclic graphs (DAGs). Define your computations, declare their dependencies, and let Fimbul handle the rest.

It automatically prevents circular references, sorts computations in the correct execution order, and caches results to eliminate redundant calculations.

Whether you're generating procedural worlds, handling data transformations, or managing complex state calculations, Fimbul helps you build clear, maintainable, and efficient computation chains.

npm version TypeScript

Table of Contents

Features

  • 🔍 Type-safe dependency graph management with full type inference and validation
  • ⚡ Automatic dependency resolution and caching
  • 🔄 Both synchronous and asynchronous computation support
  • 🎯 Zero external dependencies
  • 🧮 Optimal performance with O(n) worst-case complexity
  • 📦 Ultra-lightweight at just 518 bytes minified

Installation

npm install @fimbul-works/fimbul
# or
yarn add @fimbul-works/fimbul
# or
pnpm install @fimbul-works/fimbul

Quick Start

import Fimbul from '@fimbul-works/fimbul';

type Params = {
  greeting: string;
  recipient: string;
};

type Results = {
  greeting: string;
  punctuation: string;
  output: string;
};

const compute = Fimbul<Params, Results>();

// Define computation nodes
compute.define('greeting',
  ({ greeting, recipient }) => `${greeting} ${recipient}`
);

compute.define('punctuation',
  () => '!'
);

compute.define('output',
  (params, { greeting, punctuation }) => `${greeting}${punctuation}`,
  ['greeting', 'punctuation'] // Declare dependencies
);

// Check if nodes exist
console.log(compute.has('output')); // true
console.log(compute.has('missing')); // false

// Get a single result
const output = compute.get('output', {
  greeting: 'Hello',
  recipient: 'Fimbul'
}); // "Hello Fimbul!"

// Get multiple results at once
const results = compute.getMany(['greeting', 'output'], {
  greeting: 'Hello',
  recipient: 'Fimbul'
});
// {
//   greeting: "Hello Fimbul",
//   output: "Hello Fimbul!"
// }

Why Fimbul?

Unlike manual dependency management or reactive frameworks, Fimbul gives you:

  • Explicit control over computation flow
  • Zero overhead - no observers, no subscriptions
  • Predictable performance - O(n) complexity with perfect caching
  • Type safety - catch errors at compile time, not runtime

Core Concepts

Computation Nodes

Each node in your computation graph represents a discrete calculation unit that:

  • Takes input parameters
  • Optionally depends on other nodes
  • Produces a typed output
  • Is computed exactly once per set of parameters
  • Can be synchronous or asynchronous

Dependencies

The dependency system is designed for maximum efficiency and safety:

  • Explicit dependency declaration prevents hidden dependencies
  • Automatic topological sorting ensures correct execution order
  • Built-in cycle detection prevents infinite loops
  • Smart caching with result reuse
  • Type-safe dependency chains

Memory Management

Fimbul is designed for optimal memory usage:

  • Only stores function definitions and computed results
  • No memory leaks from circular references
  • Efficient garbage collection of unused results
  • Minimal memory footprint

Documentation

For detailed API documentation, see the API Reference.

Example: World Generator

A complete example of a simple world generator using Fimbul and simplex-noise. This example showcases how Fimbul's dependency graph can transform simple inputs into complex, interconnected world features.

import { createNoise2D } from 'simplex-noise';

type WorldGenParams = {
  x: number;
  y: number;
  noise2D: (x: number, y: number) => number;
  noiseScale: number;
};

type WorldGenResults = {
  continentShape: number;
  heightNoise: number;
  height: number;
  temperature: number;
  precipitation: number;
  biome: string;
};

const worldgen = Fimbul<WorldGenParams, WorldGenResults>();

1. Continent Shapes

First define the basic continent shapes by multiplying sine waves:

worldgen.define('continentShape',
  ({ x, y }) =>
    Math.abs(
      Math.sin(x * Math.PI * 2) * Math.sin(y * Math.PI)
    )
);

Continent Shapes

The base continent shapes create two large-scale landmasses.

2. Height Variation

Add variation to the height using noise:

worldgen.define('heightNoise',
  ({ x, y, noiseScale, noise2D }) =>
    noise2D(x * noiseScale, y * noiseScale) * 0.5 + 0.5
);

Height Noise

Noise makes the terrain more natural-looking.

3. Combined Height

Combine the continent shapes with height noise by multiplying:

worldgen.define('height',
  (params, { continentShape, heightNoise }) =>
    continentShape * heightNoise,
  ['continentShape', 'heightNoise']
);

Final Height

The result is the final elevation.

4. Temperature

Temperature varies with latitude and elevation:

worldgen.define('temperature',
  ({ y }, { height }) =>
    height > 0.4 ? y - (height - 0.4) * 2 : y,
  ['height']
);

Temperature Map

Temperature varies from poles to equator, and higher elevations are colder.

5. Precipitation

Rainfall patterns emerge from temperature:

worldgen.define('precipitation',
  (params, { temperature }) => 1 - temperature,
  ['temperature']
);

Precipitation Map

Precipitation patterns create diverse climate zones.

6. Biomes

Finally, determine biomes based on all previous factors:

worldgen.define('biome',
  (params, { height, temperature, precipitation }) => {
    if (height < 0.2023) return 'ocean';
    if (temperature >= 0.666) return 'desert';
    if (temperature > 0.42 && precipitation > 0.42) return 'rainforest';
    if (temperature > 0.3 && precipitation > 0.3) return 'forest';
    if (temperature <= 0.21) return 'tundra';
    return 'meadows';
  },
  ['height', 'temperature', 'precipitation']
);

Biome Map

The final biome map shows the rich variety of environments.

Generate World Data

const noise2D = createNoise2D();

const biome = worldgen.get(
  'biome',
  {
    x: Math.random(),
    y: Math.random(),
    noiseScale: 8,
    noise2D
  }
);

This example demonstrates Fimbul's power in managing complex, interdependent calculations. Each step builds upon previous results, creating a simple world from simple mathematical functions - all while maintaining clean, maintainable code structure.

Async Support

Fimbul provides first-class support for async computations:

import FimbulAsync from '@fimbul-works/fimbul/async';

type Params = { base: number };
type Results = { double: number, triple: number };

const compute = FimbulAsync<Params, Results>();

// Define async computation nodes
compute.define('double',
  async ({ base }) => {
    await someAsyncOperation();
    return base * 2;
  }
);

compute.define('triple',
  async ({ base }) => base * 3
);

// Get results
const result = await compute.get('double', { base: 21 }); // 42

Advanced Usage

Error Handling

Fimbul provides clear error messages for common issues:

// Attempting to define duplicate nodes
compute.define('output', fn); // OK
compute.define('output', fn); // Error: "output" already defined

// Missing dependencies
compute.define('derived', fn, ['missing']); // Error: "missing" not found

Type Safety

Fimbul leverages TypeScript's type system to catch errors at compile time:

type Params = { base: number };
type Results = { doubled: number };

const compute = Fimbul<Params, Results>();

// Type error: string is not assignable to number
compute.define('doubled', ({base}) => `${base * 2}`);

// Type error: missing dependency
compute.define('tripled', (_, {missing}) => missing * 3);

License

MIT License - See LICENSE file for details.


Built with ⚡ by FimbulWorks