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

@jamone19/nuxt-atomic

v0.1.0

Published

Nuxt 4 module for atomic (saga-like) multi-step transactions with automatic server routes, rollbacks, and logging.

Readme

@jamone19/nuxt-atomic

Atomic (saga-like) multi-step transactions for Nuxt 4 with automatic server routes, typed hydrators, rollback orchestration, and structured logging.

Atomic brings a declarative transactional workflow to Nuxt, allowing your application to daisy chain API calls into Atomic Transactions

Basics:

register steps, run, rollback safely.

  • Register a transaction → define ordered steps (execute, rollback, hydrate).
  • Call POST /api/atomic/<TransactionName> → steps run sequentially; on failure, prior steps roll back in reverse.
  • Pass data across steps: outputs accumulate (chainAcc) and GET window results join the next PUT input.
  • Works with any package manager: yarn add @jamone19/nuxt-atomic, pnpm add @jamone19/nuxt-atomic, or npm i @jamone19/nuxt-atomic.

Install

pnpm add @jamone19/nuxt-atomic
# or: yarn add @jamone19/nuxt-atomic
# or: npm i @jamone19/nuxt-atomic

Quickstart

nuxt.config.ts

export default defineNuxtConfig({
  modules: ['@jamone19/nuxt-atomic'],

  atomic: {
    baseRoute: '/api/atomic',            // optional (default '/api/atomic')
    logDir: '.nuxt-atomic/logs',         // optional
    // registryPath: '~/atomic/registry', // reserved/optional (current build inlines transactions)

    transactions: {
      CreateUser: {
        steps: [
          {
            key: 'CreateDBUser',
            execute:  { method: 'POST',   url: 'http://localhost:3001/mock/users' },
            rollback: { method: 'DELETE', url: 'http://localhost:3001/mock/users/:id' },
            hydrate:  { module: '~/atomic/steps/userCreate', export: 'hydrateCreate' }
          },
          {
            key: 'GrantWelcomeCredits',
            execute:  { method: 'POST', url: 'http://localhost:3001/mock/credits/grant' },
            rollback: { method: 'POST', url: 'http://localhost:3001/mock/credits/revoke' },
            hydrate:  { module: '~/atomic/steps/grantCredits', export: 'hydrate' }
          }
        ]
      },

      // Example of a chained flow: PUT → GET → GET → PUT
      CreateUserEverywhere: {
        steps: [
          {
            key: 'CreateInServiceA',
            execute:  { method: 'POST', url: 'http://localhost:3001/mock/users' },
            rollback: { method: 'DELETE', url: 'http://localhost:3001/mock/users/:id' },
            hydrate:  { module: '~/atomic/steps/createA', export: 'hydrate' }, // PUT
            mode: 'put'
          },
          {
            key: 'FetchAProfile',
            execute:  { method: 'GET', url: 'http://localhost:3001/mock/users/:id/profile' },
            rollback: { method: 'GET', url: 'http://localhost:3001/mock/nop' },
            hydrate:  { module: '~/atomic/steps/fetchAProfile', export: 'hydrate' }, // GET
            mode: 'get'
          },
          {
            key: 'FetchARecommendations',
            execute:  { method: 'GET', url: 'http://localhost:3001/mock/users/:id/recs' },
            rollback: { method: 'GET', url: 'http://localhost:3001/mock/nop' },
            hydrate:  { module: '~/atomic/steps/fetchARecs', export: 'hydrate' }, // GET
            mode: 'get'
          },
          {
            key: 'CreateInServiceB',
            execute:  { method: 'POST', url: 'http://localhost:3001/mock/usersB' },
            rollback: { method: 'DELETE', url: 'http://localhost:3001/mock/usersB/:id' },
            hydrate:  { module: '~/atomic/steps/createB', export: 'hydrate' }, // PUT uses original + GET window
            mode: 'put'
          }
        ]
      }
    }
  }
})

Step hydrator example ~/atomic/steps/userCreate.ts

import { createHydrator } from '@jamone19/nuxt-atomic/runtime'

type In  = { name: string; email: string; credits?: number } & Record<string, any>
type Out = { name: string; email: string }

export const hydrateCreate = createHydrator<In, Out>((input, ctx) => {
  return { name: input.name, email: input.email }
})

Run

pnpm -w build && pnpm demo
# curl
curl -sS -X POST http://localhost:3000/api/atomic/CreateUser   -H 'content-type: application/json'   -d '{"name":"Ada","email":"[email protected]","credits":50}' | jq

E2E tests

pnpm -C examples/mock-server i
pnpm -C examples/nuxt-app i
pnpm test:e2e

The suite spawns the mock server and the Nuxt dev server, then calls the atomic routes. Coverage (V8) is emitted to ./coverage and uploaded by CI as an artifact.

Roadmap

We’re looking for collaborators & sponsors to help shape the roadmap.

Planned and proposed items:

  • Retries & backoff policies (per-step and per-rollback; jitter, exponential)
  • Parallel / fan‑out steps with fan‑in joins and data merge policies
  • Idempotency keys and deduplication for execute & rollback
  • Persistence & recovery (transaction store + resume/retry after crash/redeploy)
  • Observability (OpenTelemetry traces, structured events, step timelines)
  • Circuit breakers & timeouts (fail‑fast, trip conditions, half‑open recovery)
  • Conditional branches & guards in the transaction DSL
  • Schema validation for inputs/outputs (Zod) + typed codegen for hydrators
  • DevTools panel to inspect live transactions, replay, and dry‑run
  • CLI (inspect queue, force resume, retry/abort, dump timelines)
  • Pluggable sinks for logs/metrics (stdout, file, HTTP, S3, Grafana/Loki)
  • Provider adapters (Stripe, Supabase, Clerk, SendGrid, etc.)
  • Exactly‑once patterns (outbox/inbox for webhook-driven steps)
  • Cron / scheduler for deferred retries and time‑based compensations
  • Versioning & migrations (evolve transaction definitions safely)
  • Secrets integration (Nuxt runtimeConfig, per‑step headers, signing)
  • RBAC & policy hooks for who/what can execute specific transactions

Have an idea you need? Open an issue — we’re happy to prioritize features that unblock real workloads.

Sponsorship ♥

If this project helps you ship faster, please consider sponsoring development.

  • Ko‑fi: https://ko-fi.com/jamone19

Thank you — your support helps us maintain the module, write docs, and build new features.

End-to-End Demo (with mock server)

  1. Start mock server
pnpm -w build
pnpm -C examples/mock-server i
pnpm -C examples/mock-server start
  1. Start example app
pnpm -C examples/nuxt-app i
pnpm -C examples/nuxt-app dev
  1. Call the transaction
curl -X POST http://localhost:3000/api/atomic/CreateUser   -H 'content-type: application/json'   -d '{"name":"Ada","email":"[email protected]","credits":50}'

See detailed walkthrough in docs/mock-server.md.

Run in a podman container

podman run -it --rm -p 3200-3205:3000-3005 -v $(pwd):/app:z -v /app/node_modules node:22-alpine /bin/sh
apk add --update nodejs bash vim yarn git npm pnpm curl jq
bash
cd /app

Concurrent demo runner

pnpm i
pnpm -w build
pnpm demo
# runs mock server (3001) and example Nuxt app (3000) together

# quick test
curl -X GET http://localhost:3001/health   -H 'content-type: application/json'   -d '{"name":"Ada","email":"[email protected]","credits":50}'
output:
{"ok":true}

Test demo app

Create once and capture Service A ID

RES=$(curl -sS -X POST http://localhost:3000/api/atomic/CreateUserEverywhere \
  -H 'content-type: application/json' \
  -d '{"name":"A","email":"[email protected]"}')

A_ID=$(echo "$RES" | jq -r '.results[] | select(.step=="CreateInServiceA") | .result.id')
echo "Service A ID: $A_ID"

Hit mock endpoints directly

curl -sS "http://localhost:3001/mock/users/$A_ID/profile" | jq
curl -sS "http://localhost:3001/mock/users/$A_ID/recs" | jq

Create a User across multiple services

curl -sS -X POST "http://localhost:3000/api/atomic/CreateUserEverywhere" \
  -H "content-type: application/json" \
  -d '{"name":"Alicia","email":"[email protected]"}' | jq

E2E tests (Vitest)

Requires you to install deps in both example folders.

pnpm -C examples/mock-server i
pnpm -C examples/nuxt-app i
pnpm test:e2e

The tests spin up both processes, verify CreateUser succeeds, and CreateUserFail triggers a rollback with a proper report.

Quick health/handler checks

Unknown transaction (proves handler is mounted):

curl -sS -X POST "http://localhost:3000/api/atomic/Nope" -H "content-type: application/json" -d '{}' | jq

Tail the atomic log (shows step execution & rollbacks):

tail -f examples/nuxt-app/.nuxt-atomic/logs/atomic.log

Step modes (data passing)

Optionally set mode per step:

mode?: 'put' | 'get' | 'noop' // defaults: GET -> 'get', otherwise 'put'
  • Results from all steps accumulate into chainAcc.
  • Results from get steps accumulate into a window that resets after each put.
  • Hydrators receive a composed payload:
    • For get steps: { ...original, ...chainAcc }
    • For put steps: { ...original, ...chainAcc, ...getWindowSinceLastPut } This lets you create flows like: PUT → GET → GET → GET → PUT, where the second PUT can use original input plus the outputs of the three GETs.

Typed hydrators

Import the helper and type your input/output precisely:

import { createHydrator } from '@jamone19/nuxt-atomic/runtime'

type In = { name: string; email: string } & Record<string, any>
type Out = { name: string; email: string }

export const hydrate = createHydrator<In, Out>((input, ctx) => {
  return { name: input.name, email: input.email }
})

Hydrators receive a composed input plus a context with chainAcc, windowAcc, etc.

PNPM workspace

The repo includes a workspace so one install wires everything:

pnpm i

pnpm-workspace.yaml includes examples/*, so example apps share the root lockfile and hoisted deps.

Example: PUT → GET → GET → PUT

  • Step 1 (PUT) CreateInServiceA: creates a user in Service A and returns { id, ... }.
  • Step 2 (GET) FetchAProfile: fetches a profile for :id. Result is added to the GET-window.
  • Step 3 (GET) FetchARecommendations: fetches recommendations for :id. Also added to the GET-window.
  • Step 4 (PUT) CreateInServiceB: receives original + chainAcc + windowAcc. This includes the id from Step 1 and the profile/recommendations from Steps 2–3. After this step, the GET-window is cleared.

Call it:

curl -X POST http://localhost:3000/api/atomic/CreateUserEverywhere   -H 'content-type: application/json'   -d '{"name":"Alicia","email":"[email protected]"}'

Coverage

E2E tests run in a separate process model (mock server + Nuxt app), so coverage reflects code executed directly in the test process (e.g., helpers). We still publish reports for visibility.

  • Local:

    pnpm test:e2e
    # open coverage/index.html
  • CI uploads coverage/ as an artifact named coverage-report.

Programmatic Nuxt boot in tests

The E2E suite boots Nuxt inside the Vitest process using loadNuxt + buildNuxt, and starts the mock server in-process as well. This improves V8 coverage for the server handler code paths.

Branding

SVGs live in branding/ (icon, horizontal, stacked). Drop the horizontal lockup at the top of this README:

<p align="center">
  <img src="./branding/nuxt-atomic-logo-horizontal.svg" width="640" alt="Nuxt Atomic" />
</p>

License

MIT