thefrontside / thefrontside/effection

Add AI friendly TL;DR to the docs

Open
#1,029 0 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
856
Forks
39
Avg merge
2d 8h
Merged PRs (30d)
8

Description

This TL;DR was generated (by Claude Code) from the complete Effection documentation (22,242 tokens) with the following requirements:

  • Keep under 5,000 tokens for conciseness
  • Wrap all code examples in main or run for proper execution context
  • Use resources only when return values need to be tied to parent operation lifecycle
  • Always use try/finally blocks around provide in resource examples
  • Order content in learning sequence (getting started → core concepts → advanced patterns)
  • Simplify resource pool example to avoid explicit release management

The original documentation spans 15 MDX files covering installation, TypeScript usage, core concepts, operations, spawning, resources, collections/streams, events, error handling, context, actions, scope management, and process handling.

Effection TL;DR

Structured Concurrency and Effects for JavaScript - A library that makes async JavaScript code more predictable, composable, and cancellable.

Getting Started

Installation
# Node.js/Browser
npm install effection

# Deno  
import { main } from "jsr:@effection/effection@3";
Your First Program
import { main, sleep } from 'effection';

await main(function*() {
  console.log('Hello');
  yield* sleep(1000);
  console.log('World!');
});

Core Concepts

1. Operations vs Promises
  • Operations are stateless recipes that describe what to do
  • Promises are stateful and execute immediately
  • Operations only run when evaluated with yield* or run()
import { run, until } from 'effection';

// Promise - runs immediately
const promise = fetch('/api');

// Operation - only runs when evaluated
function* fetchData() {
  return yield* until(fetch('/api'));
}

await run(fetchData);
2. The Rosetta Stone
Async/Await Effection
await yield*
async function function*
Promise Operation
new Promise() action()
for await for yield* each
AsyncIterable Stream
3. Structured Concurrency
  • No operation runs longer than its parent
  • Every operation exits fully (guaranteed cleanup)
  • Child operations are automatically cancelled when parent completes
import { main, spawn, sleep } from 'effection';

await main(function*() {
  yield* spawn(function*() {
    // This will be cancelled after 5 seconds
    for (let i = 0; i < 100; i++) {
      yield* sleep(1000);
      console.log(i);
    }
  });
  
  yield* sleep(5000); // Parent exits after 5s
});

Basic Operations

Entry Points
import { main, run } from 'effection';

// For applications
await main(function*() {
  // Your code here
});

// For integration with existing async code
await run(function*() {
  // Your code here
});
Fundamental Operations
import { main, sleep, suspend, action } from 'effection';

await main(function*() {
  // Wait for duration
  yield* sleep(1000);

  // Wait forever (until cancelled)
  yield* suspend();

  // Create custom operation
  function myOperation() {
    return action(function*(resolve) {
      let timeoutId = setTimeout(resolve, 1000);
      return () => clearTimeout(timeoutId); // cleanup
    });
  }

  yield* myOperation();
});

Concurrency

Spawning Operations
import { main, spawn, all, race } from 'effection';

await main(function*() {
  // Spawn concurrent operation
  let task = yield* spawn(longRunningOperation);
  let result = yield* task;

  // Wait for all to complete
  let [a, b, c] = yield* all([op1(), op2(), op3()]);

  // Wait for first to complete
  let winner = yield* race([op1(), op2(), op3()]);
});

Error Handling

import { main, call, spawn, suspend } from 'effection';

await main(function*() {
  try {
    yield* riskyOperation();
  } catch (error) {
    console.log('Caught:', error);
  }

  // Error boundaries for background tasks
  try {
    yield* call(function*() {
      yield* spawn(riskyBackgroundTask); // Errors bubble up
      yield* suspend();
    });
  } catch (error) {
    console.log('Background task failed:', error);
  }
});

Events

import { main, on, once, each } from 'effection';

await main(function*() {
  // Single event
  let event = yield* once(button, 'click');

  // Stream of events
  for (let click of yield* each(on(button, 'click'))) {
    console.log('clicked!');
    yield* each.next();
  }
});

Streams & Channels

import { main, createChannel, each, spawn } from 'effection';

await main(function*() {
  // Channels for custom streams
  let channel = createChannel();
  yield* spawn(function*() {
    yield* channel.send('message 1');
    yield* channel.send('message 2');
  });

  for (let msg of yield* each(channel)) {
    console.log(msg);
    yield* each.next();
  }
});

Resources

Use resources when the return value needs to be tied to the lifecycle of the parent operation:

import { main, resource, once } from 'effection';

function useSocket(url) {
  return resource(function*(provide) {
    let socket = new WebSocket(url);
    
    // Setup
    yield* once(socket, 'open');
    
    try {
      yield* provide(socket); // Give socket to caller
    } finally {
      socket.close(); // Cleanup guaranteed
    }
  });
}

// Usage
await main(function*() {
  let socket = yield* useSocket('ws://localhost');
  socket.send('hello');
  // Socket automatically closed when scope exits
});

Context

Share values across operation tree without passing parameters:

import { main, createContext } from 'effection';

const DatabaseContext = createContext('db');

await main(function*() {
  yield* DatabaseContext.set(database);
  yield* processUsers();
});

function* processUsers() {
  let db = yield* DatabaseContext.expect();
  // Use db here
}

Common Patterns

Timeout Any Operation
import { main, race, sleep } from 'effection';

function withTimeout(operation, ms) {
  return race([
    operation,
    function*() {
      yield* sleep(ms);
      throw new Error('Timeout');
    }
  ]);
}

await main(function*() {
  yield* withTimeout(slowOperation(), 5000);
});
Retry with Backoff
import { main, sleep } from 'effection';

function* retry(operation, maxAttempts = 3) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return yield* operation();
    } catch (error) {
      if (attempt === maxAttempts) throw error;
      yield* sleep(1000 * attempt); // Exponential backoff
    }
  }
}

await main(function*() {
  let result = yield* retry(unreliableOperation);
});
Resource Pool
import { main, resource, sleep } from 'effection';

function usePool(createResource, size = 5) {
  return resource(function*(provide) {
    let pool = [];
    let inUse = new Set();
    
    // Pre-create resources
    for (let i = 0; i < size; i++) {
      pool.push(yield* createResource());
    }
    
    try {
      yield* provide({
        *acquire() {
          while (pool.length === 0) {
            yield* sleep(10);
          }
          let resource = pool.pop();
          inUse.add(resource);
          return resource;
        }
      });
    } finally {
      // Clean up all resources
      for (let resource of [...pool, ...inUse]) {
        resource.cleanup();
      }
    }
  });
}

await main(function*() {
  let pool = yield* usePool(createDatabaseConnection, 10);
  let conn = yield* pool.acquire();
  // Use connection - it's automatically cleaned up when scope exits
});

Key Benefits

  1. Automatic Cleanup - No leaked timeouts, requests, or resources
  2. Predictable Cancellation - Operations can always be interrupted
  3. Composability - Operations compose like UI components
  4. Familiar Syntax - Uses standard JavaScript constructs
  5. Type Safety - Full TypeScript support

That's Effection in a nutshell! It brings structured concurrency to JavaScript, making async code more reliable and easier to reason about.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Review the 15 MDX documentation files and the supplied TL;DR requirements first. Add the concise TL;DR in the appropriate documentation location, preserving the requested learning order and execution-context guidance; done means the documentation includes the under-5,000-token summary and its examples follow the stated resource and code-wrapping rules.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
documentation
Issue type
Documentation
Difficulty
3/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.