open-index/open-npm huggingface dataset:
npm package: @saga-bus/core
Description: Core types, DSL, and runtime for saga-bus message orchestration
Latest version: 0.2.2

# @saga-bus/core

Core types, interfaces, and runtime for the saga-bus message orchestration library.

## Installation

```bash
pnpm add @saga-bus/core
```

## Quick Start

```typescript
import { createSagaMachine, createBus } from "@saga-bus/core";
import { InMemoryTransport } from "@saga-bus/transport-inmemory";
import { InMemorySagaStore } from "@saga-bus/store-inmemory";

// Define your saga
const orderSaga = createSagaMachine<OrderState, OrderMessages>()
  .name("OrderSaga")
  .correlate("OrderSubmitted", (msg) => msg.orderId, { canStart: true })
  .correlate("PaymentCaptured", (msg) => msg.orderId)
  .initial<OrderSubmitted>((msg) => ({
    orderId: msg.orderId,
    status: "pending",
  }))
  .on("PaymentCaptured")
    .handle(async (msg, ctx) => {
      ctx.updateState({ ...ctx.state, status: "paid" });
      ctx.complete();
    })
  .build();

// Create and start the bus
const bus = createBus({
  transport: new InMemoryTransport(),
  store: new InMemorySagaStore(), // shared across all sagas
  sagas: [{ definition: orderSaga }],
});

await bus.start();
await bus.publish({ type: "OrderSubmitted", orderId: "123" });
```

## Store Configuration

You can configure stores in two ways:

**Shared store (recommended):**
```typescript
const bus = createBus({
  transport,
  store: new PostgresSagaStore({ pool }), // used by all sagas
  sagas: [
    { definition: orderSaga },
    { definition: paymentSaga },
  ],
});
```

**Per-saga stores (with override):**
```typescript
const bus = createBus({
  transport,
  store: new PostgresSagaStore({ pool }), // default
  sagas: [
    { definition: orderSaga },                     // uses default
    { definition: auditSaga, store: auditStore }, // override
  ],
});
```

## Exports

### Types

- `BaseMessage` - Base interface for all messages
- `MessageEnvelope` - Wrapper with metadata
- `Transport` - Transport abstraction
- `SagaStore` - State persistence abstraction
- `SagaDefinition` - Saga definition interface
- `Bus` - Main bus interface

### Functions

- `createSagaMachine<TState, TMessages>()` - Fluent saga builder
- `createBus(config)` - Create bus instance

### Errors

- `ConcurrencyError` - Optimistic locking conflict
- `TransientError` - Retriable error

## License

MIT
npm package: @saga-bus/examples-shared
Description: Shared types, messages, and sagas for saga-bus examples
Latest version: 0.2.2

# @saga-bus/examples-shared

Shared types, messages, and saga definitions used by saga-bus example applications.

## Contents

### Messages

Order lifecycle messages for the example Order Saga:

- `OrderSubmitted` - Initial order creation
- `PaymentRequested` / `PaymentCaptured` / `PaymentFailed`
- `InventoryReserveRequested` / `InventoryReserved` / `InventoryReservationFailed`
- `ShipmentRequested` / `ShipmentCreated`
- `OrderCancelled` / `OrderCompleted`

### Sagas

**OrderSaga** - Complete order fulfillment saga demonstrating:
- Multi-step workflow coordination
- State guards for status-based transitions
- Compensation (cancellation) handling
- Command publishing to downstream services

### Types

- `OrderSagaState` - Saga state with order details and status tracking
- `OrderStatus` - Order lifecycle states
- `OrderItem` - Line item structure

## Usage

```typescript
import {
  OrderSaga,
  OrderSagaState,
  OrderSubmitted,
  OrderMessage
} from "@saga-bus/examples-shared";
```

## License

MIT
npm package: @saga-bus/middleware-logging
Description: Structured logging middleware for saga-bus
Latest version: 0.2.2

# @saga-bus/middleware-logging

Structured logging middleware for saga-bus.

## Installation

```bash
pnpm add @saga-bus/middleware-logging
```

## Usage

```typescript
import { createLoggingMiddleware } from "@saga-bus/middleware-logging";
import { createBus } from "@saga-bus/core";

const loggingMiddleware = createLoggingMiddleware({
  logger: console,
  level: "info",
});

const bus = createBus({
  middleware: [loggingMiddleware],
  sagas: [...],
  transport,
});
```

## Log Events

| Event | Level | Description |
|-------|-------|-------------|
| `saga.message.received` | info | Message received for processing |
| `saga.handler.start` | debug | Handler execution started |
| `saga.handler.success` | info | Handler completed successfully |
| `saga.handler.error` | error | Handler threw an error |
| `saga.state.created` | info | New saga instance created |
| `saga.state.completed` | info | Saga marked as complete |

## Configuration

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `logger` | `Logger` | `console` | Logger instance |
| `level` | `string` | `"info"` | Minimum log level |
| `includeState` | `boolean` | `false` | Include state in logs |

## License

MIT
npm package: @saga-bus/middleware-tenant
Description: Multi-tenant middleware for saga-bus
Latest version: 0.2.2

# @saga-bus/middleware-tenant

Multi-tenant isolation middleware for saga-bus.

## Installation

```bash
pnpm add @saga-bus/middleware-tenant
```

## Usage

```typescript
import { createTenantMiddleware, getTenantId } from "@saga-bus/middleware-tenant";
import { createBus } from "@saga-bus/core";

const tenantMiddleware = createTenantMiddleware({
  strategy: "header",
  headerName: "x-tenant-id",
  required: true,
});

const bus = createBus({
  middleware: [tenantMiddleware],
  sagas: [...],
  transport,
});

// Access tenant in handlers
const tenantId = getTenantId(); // from AsyncLocalStorage context
```

## Tenant Resolution Strategies

### Header Strategy (default)
Extracts tenant ID from message headers:

```typescript
createTenantMiddleware({
  strategy: "header",
  headerName: "x-tenant-id",
});
```

### Correlation Prefix Strategy
Extracts tenant ID from correlation ID prefix:

```typescript
createTenantMiddleware({
  strategy: "correlation-prefix",
  correlationSeparator: ":",
});
// correlationId "tenant123:order-456" → tenantId "tenant123"
```

### Custom Strategy
Provide your own resolver function:

```typescript
createTenantMiddleware({
  strategy: "custom",
  resolver: (envelope) => envelope.payload?.tenantId,
});
```

## Tenant-Aware Publishing

```typescript
import { createTenantPublisher } from "@saga-bus/middleware-tenant";

const publish = createTenantPublisher(bus, {
  headerName: "x-tenant-id",
});

// Automatically adds tenant header from context
await publish({ type: "OrderCreated", payload: { orderId: "123" } });
```

## Configuration

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `strategy` | `string` | `"header"` | Resolution strategy |
| `headerName` | `string` | `"x-tenant-id"` | Header name for header strategy |
| `correlationSeparator` | `string` | `":"` | Separator for correlation prefix |
| `resolver` | `function` | - | Custom resolver function |
| `required` | `boolean` | `true` | Require tenant on all messages |
| `defaultTenantId` | `string` | - | Default when not required |
| `allowedTenants` | `string[]` | - | Allowlist of valid tenants |
| `prefixSagaId` | `boolean` | `true` | Prefix saga IDs with tenant |

## Context API

```typescript
import {
  getTenantId,
  getTenantInfo,
  requireTenantId,
  runWithTenant,
} from "@saga-bus/middleware-tenant";

// Get current tenant (undefined if not set)
const tenantId = getTenantId();

// Get full tenant info
const info = getTenantInfo(); // { tenantId, originalSagaId }

// Throw if no tenant
const tenantId = requireTenantId();

// Run code with explicit tenant context
await runWithTenant("tenant-123", async () => {
  // getTenantId() returns "tenant-123" here
});
```

## License

MIT
npm package: @saga-bus/middleware-tracing
Description: OpenTelemetry tracing middleware for saga-bus
Latest version: 0.2.2

# @saga-bus/middleware-tracing

OpenTelemetry distributed tracing middleware for saga-bus.

## Installation

```bash
pnpm add @saga-bus/middleware-tracing @opentelemetry/api
```

## Usage

```typescript
import { createTracingMiddleware } from "@saga-bus/middleware-tracing";
import { createBus } from "@saga-bus/core";
import { trace } from "@opentelemetry/api";

const tracingMiddleware = createTracingMiddleware({
  tracer: trace.getTracer("my-service"),
});

const bus = createBus({
  middleware: [tracingMiddleware],
  sagas: [...],
  transport,
});
```

## Features

- Automatic span creation for message processing
- W3C Trace Context propagation (traceparent/tracestate headers)
- Span attributes for message type, saga ID, correlation ID
- Error recording on handler failures
- Optional payload recording

## Trace Context Propagation

The middleware automatically propagates trace context across service boundaries:

```typescript
import { createPublishTracer } from "@saga-bus/middleware-tracing";

const publishWithTracing = createPublishTracer(bus, {
  tracer: trace.getTracer("my-service"),
});

// Automatically injects traceparent/tracestate headers
await publishWithTracing({
  type: "OrderCreated",
  payload: { orderId: "123" },
});
```

## Span Attributes

Each message processing span includes:

| Attribute | Description |
|-----------|-------------|
| `messaging.system` | `"saga-bus"` |
| `messaging.operation` | `"process"` |
| `messaging.message.type` | Message type |
| `saga.name` | Saga name |
| `saga.id` | Saga instance ID |
| `messaging.message.correlation_id` | Correlation ID |

## Configuration

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `tracer` | `Tracer` | global | OpenTelemetry tracer |
| `tracerName` | `string` | `"@saga-bus/middleware-tracing"` | Tracer name |
| `recordPayload` | `boolean` | `false` | Record payload as attribute |
| `maxPayloadSize` | `number` | `1024` | Max payload size to record |
| `attributeExtractor` | `function` | - | Custom attribute extractor |

## Custom Attributes

```typescript
createTracingMiddleware({
  attributeExtractor: (envelope) => ({
    "custom.order_id": envelope.payload?.orderId,
    "custom.priority": envelope.payload?.priority,
  }),
});
```

## License

MIT
npm package: @saga-bus/nestjs
Description: NestJS module for saga-bus
Latest version: 0.2.2

# @saga-bus/nestjs

NestJS module for saga-bus integration.

## Installation

```bash
pnpm add @saga-bus/nestjs
```

## Usage

### Basic Setup

```typescript
// app.module.ts
import { Module } from "@nestjs/common";
import { SagaBusModule } from "@saga-bus/nestjs";
import { InMemoryTransport } from "@saga-bus/transport-inmemory";
import { InMemorySagaStore } from "@saga-bus/store-inmemory";

@Module({
  imports: [
    SagaBusModule.forRoot({
      transport: new InMemoryTransport(),
      store: new InMemorySagaStore(),
    }),
  ],
})
export class AppModule {}
```

### Async Configuration

```typescript
SagaBusModule.forRootAsync({
  imports: [ConfigModule],
  useFactory: (config: ConfigService) => ({
    transport: new RabbitMqTransport({
      uri: config.get("RABBITMQ_URI"),
    }),
    store: new PostgresSagaStore({
      pool: new Pool({ connectionString: config.get("DATABASE_URL") }),
    }),
  }),
  inject: [ConfigService],
});
```

## Decorators

### @MessageHandler

Handle specific message types:

```typescript
import { Injectable } from "@nestjs/common";
import { MessageHandler } from "@saga-bus/nestjs";

@Injectable()
export class OrderHandlers {
  @MessageHandler("OrderCreated")
  async handleOrderCreated(message: OrderCreated) {
    console.log("Order created:", message.payload);
  }

  @MessageHandler("OrderShipped")
  async handleOrderShipped(message: OrderShipped) {
    console.log("Order shipped:", message.payload);
  }
}
```

### @InjectSagaBus

Inject the saga bus service:

```typescript
import { Injectable } from "@nestjs/common";
import { InjectSagaBus, SagaBusService } from "@saga-bus/nestjs";

@Injectable()
export class OrderService {
  constructor(@InjectSagaBus() private readonly sagaBus: SagaBusService) {}

  async createOrder(data: CreateOrderDto) {
    await this.sagaBus.publish(
      { type: "OrderCreated", payload: data },
      { endpoint: "orders" }
    );
  }
}
```

## SagaBusService API

```typescript
// Publish a message
await sagaBus.publish(message, options);

// Get underlying transport
const transport = sagaBus.getTransport();

// Get underlying store
const store = sagaBus.getStore();

// Get middleware pipeline
const middleware = sagaBus.getMiddleware();
```

## Configuration

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `transport` | `Transport` | required | Transport implementation |
| `store` | `SagaStore` | required | Saga store implementation |
| `middleware` | `SagaMiddleware[]` | `[]` | Middleware pipeline |
| `autoStart` | `boolean` | `true` | Auto-start transport |
| `autoStop` | `boolean` | `true` | Auto-stop on destroy |

## Lifecycle

The module automatically:

- Starts the transport on module init (if `autoStart: true`)
- Discovers and registers all `@MessageHandler` decorated methods
- Stops the transport on module destroy (if `autoStop: true`)

## License

MIT
npm package: @saga-bus/store-dynamodb
Description: DynamoDB saga store for saga-bus
Latest version: 0.2.2

# @saga-bus/store-dynamodb

AWS DynamoDB saga store for saga-bus.

## Installation

```bash
pnpm add @saga-bus/store-dynamodb @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
```

## Usage

```typescript
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
import { DynamoDBSagaStore, createTable } from "@saga-bus/store-dynamodb";
import { createBus } from "@saga-bus/core";

const dynamoClient = new DynamoDBClient({ region: "us-east-1" });
const docClient = DynamoDBDocumentClient.from(dynamoClient);

// Create table (run once, or use CloudFormation/CDK)
await createTable(dynamoClient, { tableName: "saga-instances" });

const store = new DynamoDBSagaStore({
  client: docClient,
  tableName: "saga-instances",
});

const bus = createBus({
  sagas: [{ definition: mySaga, store }],
  transport,
});
```

## Table Schema

The store uses a single-table design:

| Key | Pattern | Description |
|-----|---------|-------------|
| `PK` | `SAGA#<sagaName>` | Partition key |
| `SK` | `ID#<sagaId>` | Sort key |
| `GSI1PK` | `SAGA#<sagaName>` | Correlation index PK |
| `GSI1SK` | `CORR#<correlationId>` | Correlation index SK |
| `GSI2PK` | `COMPLETED#<date>` | Cleanup index PK |
| `GSI2SK` | `<sagaName>#<sagaId>` | Cleanup index SK |

## Features

- Single-table design for cost efficiency
- Optimistic concurrency via conditional writes
- GSI for correlation ID lookups
- GSI for cleanup queries by completion date
- Automatic GSI index management

## Configuration

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `client` | `DynamoDBDocumentClient` | required | DynamoDB Document Client |
| `tableName` | `string` | required | Table name |
| `correlationIndexName` | `string` | `"GSI1"` | Correlation GSI name |
| `cleanupIndexName` | `string` | `"GSI2"` | Cleanup GSI name |

## Sharing Across Sagas

A single store instance can be shared across multiple sagas:

```typescript
const store = new DynamoDBSagaStore({ client, tableName: "saga-instances" });

const bus = createBus({
  transport,
  store, // shared by all sagas
  sagas: [
    { definition: orderSaga },
    { definition: paymentSaga },
  ],
});
```

Data is isolated by `sagaName` in the partition key, so different saga types won't conflict.

## Table Management

```typescript
import { createTable, deleteTable, getTableSchema } from "@saga-bus/store-dynamodb";

// Create with defaults
await createTable(dynamoClient, { tableName: "saga-instances" });

// Get schema for CloudFormation/CDK
const schema = getTableSchema("saga-instances");

// Delete table
await deleteTable(dynamoClient, "saga-instances");
```

## License

MIT
npm package: @saga-bus/store-inmemory
Description: In-memory saga store for saga-bus testing and development
Latest version: 0.2.2

# @saga-bus/store-inmemory

In-memory saga store for testing and development.

## Installation

```bash
pnpm add @saga-bus/store-inmemory
```

## Usage

```typescript
import { InMemorySagaStore } from "@saga-bus/store-inmemory";
import { createBus } from "@saga-bus/core";

const store = new InMemorySagaStore();

const bus = createBus({
  sagas: [{ definition: mySaga, store }],
  transport,
});
```

## Features

- Optimistic concurrency control
- Correlation ID indexing
- State versioning
- Zero configuration
- Instant operations

## When to Use

**Use for:**
- Unit and integration tests
- Local development
- Prototyping

**Do not use for:**
- Production deployments
- Multi-instance applications
- Data that needs to survive restarts

## Limitations

- **No persistence**: All data is lost when the process exits
- **Single process only**: State is not shared between Node.js processes
- **Memory bound**: Large numbers of sagas may consume significant memory

For production, use a persistent store like [@saga-bus/store-postgres](../store-postgres), [@saga-bus/store-mongo](../store-mongo), or [@saga-bus/store-dynamodb](../store-dynamodb).

## Sharing Across Sagas

A single store instance can be shared across multiple sagas within the same process:

```typescript
const store = new InMemorySagaStore();

const bus = createBus({
  transport,
  store, // shared by all sagas
  sagas: [
    { definition: orderSaga },
    { definition: paymentSaga },
  ],
});
```

Data is isolated by `sagaName`, so different saga types won't conflict.

## License

MIT
npm package: @saga-bus/store-mongo
Description: MongoDB saga store for saga-bus
Latest version: 0.2.2

# @saga-bus/store-mongo

MongoDB saga store for saga-bus.

## Installation

```bash
pnpm add @saga-bus/store-mongo mongodb
```

## Usage

```typescript
import { MongoClient } from "mongodb";
import { MongoSagaStore } from "@saga-bus/store-mongo";
import { createBus } from "@saga-bus/core";

const client = new MongoClient("mongodb://localhost:27017");
await client.connect();

const store = new MongoSagaStore({
  db: client.db("myapp"),
  collectionName: "saga_instances",
});

const bus = createBus({
  sagas: [{ definition: mySaga, store }],
  transport,
});
```

## Document Schema

Documents are stored with this structure:

```typescript
{
  _id: "OrderSaga:saga-123",      // Composite key
  sagaName: "OrderSaga",
  sagaId: "saga-123",
  correlationId: "order-456",
  version: 1,
  isCompleted: false,
  state: { /* saga state */ },
  createdAt: ISODate("..."),
  updatedAt: ISODate("...")
}
```

## Indexes

The store automatically creates indexes on:

- `_id` (primary key)
- `sagaName + correlationId` (for correlation lookups)
- `isCompleted + updatedAt` (for cleanup queries)

## Features

- Optimistic concurrency via version field
- Atomic updates with `findOneAndUpdate`
- Efficient correlation ID lookups
- TTL-based cleanup support

## Configuration

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `db` | `Db` | required | MongoDB database instance |
| `collectionName` | `string` | `"saga_instances"` | Collection name |

## Sharing Across Sagas

A single store instance can be shared across multiple sagas:

```typescript
const store = new MongoSagaStore({ db });

const bus = createBus({
  transport,
  store, // shared by all sagas
  sagas: [
    { definition: orderSaga },
    { definition: paymentSaga },
  ],
});
```

Data is isolated by `sagaName` in the collection, so different saga types won't conflict.

## Cleanup

Query completed sagas for cleanup:

```typescript
const completedSagas = await db
  .collection("saga_instances")
  .find({
    isCompleted: true,
    updatedAt: { $lt: thirtyDaysAgo },
  })
  .toArray();
```

## License

MIT
npm package: @saga-bus/store-postgres
Description: PostgreSQL saga store for saga-bus using native pg driver
Latest version: 0.2.2

# @saga-bus/store-postgres

PostgreSQL saga store using native pg driver.

## Installation

```bash
pnpm add @saga-bus/store-postgres pg
```

## Usage

```typescript
import { Pool } from "pg";
import { PostgresSagaStore, createSchema } from "@saga-bus/store-postgres";
import { createBus } from "@saga-bus/core";

const pool = new Pool({
  connectionString: "postgresql://user:pass@localhost:5432/mydb",
});

// Initialize schema (run once)
await createSchema(pool);

const store = new PostgresSagaStore({ pool });

const bus = createBus({
  sagas: [{ definition: mySaga, store }],
  transport,
});
```

## Database Schema

The `createSchema` function creates:

```sql
CREATE TABLE saga_instances (
  id             VARCHAR(128) NOT NULL,
  saga_name      VARCHAR(128) NOT NULL,
  correlation_id VARCHAR(256) NOT NULL,
  version        INTEGER NOT NULL,
  is_completed   BOOLEAN NOT NULL DEFAULT FALSE,
  state          JSONB NOT NULL,
  created_at     TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at     TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  PRIMARY KEY (saga_name, id)
);
```

## Features

- Optimistic concurrency with version column
- JSONB state storage
- Connection pooling
- Indexed lookups by correlation ID
- Cleanup helpers for completed sagas

## Configuration

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `pool` | `Pool` | required | pg Pool instance |
| `schema` | `string` | `"public"` | Database schema |
| `tableName` | `string` | `"saga_instances"` | Table name |

## Sharing Across Sagas

A single store instance can be shared across multiple sagas:

```typescript
const store = new PostgresSagaStore({ pool });

const bus = createBus({
  transport,
  store, // shared by all sagas
  sagas: [
    { definition: orderSaga },
    { definition: paymentSaga },
  ],
});
```

Data is isolated by `saga_name` in the database, so different saga types won't conflict.

## License

MIT
npm package: @saga-bus/store-prisma
Description: Prisma ORM adapter for saga-bus state storage
Latest version: 0.2.2

# @saga-bus/store-prisma

Prisma ORM adapter for saga-bus state storage.

## Installation

```bash
pnpm add @saga-bus/store-prisma @prisma/client
```

## Prisma Schema

Add to your `schema.prisma`:

```prisma
model SagaInstance {
  id            String   @db.VarChar(128)
  sagaName      String   @map("saga_name") @db.VarChar(128)
  correlationId String   @map("correlation_id") @db.VarChar(256)
  version       Int
  isCompleted   Boolean  @default(false) @map("is_completed")
  state         Json

  createdAt     DateTime @default(now()) @map("created_at")
  updatedAt     DateTime @updatedAt @map("updated_at")

  @@id([sagaName, id])
  @@unique([sagaName, correlationId])
  @@index([sagaName, isCompleted])
  @@map("saga_instances")
}
```

## Usage

```typescript
import { PrismaClient } from "@prisma/client";
import { PrismaSagaStore } from "@saga-bus/store-prisma";
import { createBus } from "@saga-bus/core";

const prisma = new PrismaClient();
const store = new PrismaSagaStore({ prisma });

const bus = createBus({
  sagas: [{ definition: mySaga, store }],
  transport,
});
```

## Features

- Uses your existing PrismaClient
- Optimistic concurrency control
- Type-safe state serialization
- Works with any Prisma-supported database

## Sharing Across Sagas

A single store instance can be shared across multiple sagas:

```typescript
const store = new PrismaSagaStore({ prisma });

const bus = createBus({
  transport,
  store, // shared by all sagas
  sagas: [
    { definition: orderSaga },
    { definition: paymentSaga },
  ],
});
```

Data is isolated by `sagaName` in the database, so different saga types won't conflict.

## License

MIT