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

@lyght/saga

v0.0.1

Published

Local transaction management library with rollback support for Node.js applications

Readme

LocalTransaction Utility

개요

해당 유틸리티는 Node.js 환경에서 SAGA 패턴에 대응하는 로컬 트랜잭션(Local Transaction)간단하고 선언적인 방식으로 구현하기 위한 유틸리티입니다.

  • 주요 목적
    1. 단계적(체이닝) 작업을 수행하다가, 어떤 단계에서 실패(에러)하면 이전 단계들을 보상(rollback) 할 수 있도록 지원합니다.
    2. 데코레이터(@LocalTransaction) 를 이용하면 메서드 범위에서 트랜잭션 컨텍스트를 자동 생성 및 에러 발생 시 rollbackAll 실행을 보장합니다.
    3. withRollback 함수를 통해 동기/비동기 작업을 프로미스로 래핑하고, 역순 실행되는 보상 함수를 간편하게 등록할 수 있습니다.

프로젝트 구조

이 프로젝트는 monorepo 구조로 구성되어 있습니다:

saga/
├── src/                          # 라이브러리 소스 코드
│   ├── index.ts                  # 메인 export 파일
│   └── LocalTransaction/         # LocalTransaction 유틸리티들
├── example/                      # 예제 애플리케이션 (별도 패키지)
│   ├── package.json             # example 패키지 설정
│   ├── index.ts                 # 예제 실행 파일
│   └── *.ts                     # 예제 서비스 파일들
├── package.json                 # 루트 패키지 (workspaces 설정)
└── dist/                        # 컴파일된 라이브러리 출력

설치 및 실행

  1. 의존성 설치:

    pnpm install
  2. 라이브러리 빌드:

    pnpm build
  3. 예제 실행:

    cd example
    pnpm start

특징

  • AsyncLocalStorage 사용
    • 각 요청/비동기 흐름마다 독립된 트랜잭션 컨텍스트를 관리.
    • 데코레이터가 없을 때도 수동으로 localTransactionContextStorage.run(...)으로 컨텍스트 범위를 지정할 수 있음.
  • 동기/비동기 함수 모두 처리 가능
    • withRollback(action)에서 동기/비동기 action과, .rollback(fn)에서 동기/비동기 fn을 모두 지원
  • 단순화된 "부분 롤백"
    • 단계별 보상 로직(rollbackFn)을 등록하고, 에러 발생 시 진행 상황까지의 동작을 등록 순서의 역순으로 실행

사용 예시

1. TypeScript + 데코레이터 방식

아래 예시는 TypeScript 코드에서 @LocalTransaction 데코레이터를 사용해 로컬 트랜잭션을 자동으로 생성하고,
withRollback 체이닝을 통해 보상 로직(rollback)을 등록하는 대표적인 구조입니다.

import {LocalTransaction} from './LocalTransactionDecorator';
import {withRollback} from './withRollback';

@LocalTransaction({catchUnhandledError: true})
async function sagaMethod(): Promise<void> {
    // 1) 동기 작업 + rollback 등록
    const syncResult = await withRollback(getFoo())
        .rollback(() => syncRollback());
    console.log('syncResult =', syncResult);  // 'Foo'

    // 2) 비동기 작업 + rollback 등록
    const asyncResult = await withRollback(gerBar())
        .rollback(() => asyncRollback());
    console.log('asyncResult =', asyncResult);  // 'getBar'

    throw new Error('[sagaMethod] error for testing');
}

설명

  • @LocalTransaction 데코레이터가 메서드 실행 전후로 트랜잭션 컨텍스트를 생성/획득합니다.
  • 내부에서 에러가 발생하면, 이미 등록된 rollback 함수들이 역순으로 호출됩니다.
  • 동기 작업/비동기 작업 모두 withRollback(...) + .rollback(...) 체이닝으로 보상 로직을 붙일 수 있습니다.

2. JavaScript + (No Decorator) 방식

데코레이터를 사용할 수 없는 환경(혹은 Plain JS)이라면, 직접 컨텍스트를 생성하고 withRollback을 호출해 보상 로직을 등록할 수 있습니다.

const {localTransactionContextStorage} = require('./LocalTransactionContextStorage');
const {LocalTransactionContext} = require('./LocalTransactionContext');
const {withRollback} = require('./withRollback');

async function sagaMethodPlainJS() {
    // 1) 새 컨텍스트 만들기
    const ctx = new LocalTransactionContext();

    try {
        // 2) localTransactionContextStorage.run을 통해 이 컨텍스트 범위 내에서 작업
        await localTransactionContextStorage.run(ctx, async () => {
            // 동기
            const syncResult = await withRollback(getFoo())
                .rollback(() => syncRollback());
            console.log('syncResult =', syncResult);

            // 비동기
            const asyncResult = await withRollback(getBar())
                .rollback(() => asyncRollback());
            console.log('asyncResult =', asyncResult);

            // 에러발생
            throw new Error('[sagaMethodPlainJS] error for testing');
        });
    } catch (error) {
        console.error('[ERROR] sagaMethodPlainJS:', error.message);

        // 3) rollbackAll() 실행
        if (ctx.hasRollbacks() && !ctx.isExecuted()) {
            console.log('[ROLLBACK] Executing rollbackAll');
            await ctx.rollbackAll();
        }
    }
}

설명

  • localTransactionContextStorage.run(ctx, callback): 해당 콜백 실행 중에는 ctx가 활성 컨텍스트가 됩니다.
  • withRollback(...).rollback(...)을 호출할 때 현재 활성 컨텍스트에 보상 함수를 추가합니다.
  • 콜백 내부에서 에러가 발생하면, 상위에서 수동으로 ctx.rollbackAll()을 호출하거나, 별도의 에러 핸들링 로직에 포함시킬 수 있습니다. (데코레이터 없이 직접 제어 가능)

await 위치에 대한 주의사항

  1. withRollback는 항상 Promise를 반환하므로, 결과(T)를 얻기 위해서는 await해야 합니다.

    const result = await withRollback(someAction());
  2. 동기 함수withRollback(fn()) 형태로 감싸면,

    • 내부적으로 Promise.resolve(fn()) 처리 → 반환값도 Promise.
    • 따라서 await으로 풀어야 결과를 사용할 수 있습니다.
  3. rollback 함수는 에러가 발생했을 때 내부에서 자동 실행되므로, 롤백 함수 호출에 대해 직접 await할 필요는 없습니다.

    • .rollback(fn)은 "보상 로직 등록"만 수행.
    • 실제 실행은 "에러 발생 시점" → "데코레이터나 수동 rollbackAll()에서 역순 호출"

구조

flowchart TD
subgraph Application Layer
A[애플리케이션 코드비즈니스 로직]
B[서비스 메서드 @LocalTransaction 적용]
end

subgraph Transaction Utilities
C[LocalTransactionDecorator]
D[withRollback Utility]
E[LocalTransactionContext]
F[Rollback Functions Stack]
end

subgraph Context Management
G[AsyncLocalStorage]
end

A --> B
B --> C
C -->|컨텍스트 생성/획득|G
G --> E
C -->|메서드 실행 시 에러 캐치|E
D -->|보상 함수 등록|E
E --> F
E -->|rollbackAll 호출|F
F -->|역순 실행|E
E -->|에러 발생 시 rollbackAll|C

%% Additional flows:
B --- D

블로그 article