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

@othree.io/chisel-sdk

v7.2.1

Published

Utility functions for Chisel

Readme

@othree.io/chisel-sdk

SDK for building event-sourced microservices with Chisel on AWS. Provides pre-wired write-side actors, read-side projections, and CDK infrastructure stacks out of the box.

Installation

npm install @othree.io/chisel-sdk

Exports

import { aws, iac } from '@othree.io/chisel-sdk'

| Namespace | Purpose | |-----------|---------| | aws | Runtime Lambda handlers for write-side (actor) and read-side (projections) | | iac | CDK stack factories for the full CQRS infrastructure |

Write Side (aws.createActor)

Creates a fully wired Chisel actor with DynamoDB event persistence, sharded SNS publishing, and X-Ray tracing.

import { aws } from '@othree.io/chisel-sdk'

const actor = aws.createActor<MyAggregate, MyEvent>({
    boundedContext: 'Orders',
    now: () => Date.now(),
    getInitialState: async (contextId) => Optional(emptyOrder),
    handleCommand: myCommandHandler,
    reduceEvent: myReducer,
})

// Lambda exports
export const handleCommand = actor.handleCommand
export const getState = actor.getState

Environment variables (read automatically):

| Variable | Description | |----------|-------------| | EVENTS_TABLE | DynamoDB events table name | | EVENTS_TOPICS | Comma-separated SNS topic ARNs | | LOG_LEVEL | Logging level (default: info) |

Returns a LambdaActor<AggregateRoot> with:

  • handleCommand - processes a LambdaCommand and returns an InvocationResult<CommandResult<AggregateRoot>>
  • getState - loads and returns the current aggregate state as InvocationResult<AggregateRoot> by contextId

Read Side (aws.getProjectionServices)

Creates a fully wired projection processor with DynamoDB upsert/delete, SQS batch processing, and DLQ routing.

import { aws } from '@othree.io/chisel-sdk'

const projection = aws.getProjectionServices<MyAggregate, MyEvent>({
    boundedContext: 'Orders',
    now: () => Date.now(),
})

// Lambda exports
export const updateProjection = projection.sqs.updateProjection
export const getById = projection.services.getById

Environment variables (read automatically):

| Variable | Description | |----------|-------------| | PROJECTION_TABLE | DynamoDB projection table name | | PROJECTION_DLQ | Application DLQ URL | | PROJECTION_PK | Projection table partition key name | | DELETE_ON_EVENTS | Comma-separated event types that trigger deletion | | LOG_LEVEL | Logging level (default: info) |

Returns:

  • sqs.updateProjection - SQS batch handler that upserts or deletes projections based on event type
  • services.getById - queries the projection table by ID

Standalone Functions

These are also exported for direct use or custom wiring:

| Function | Description | |----------|-------------| | aws.getProcessInput | Parses an SQS record containing an SNS-wrapped Chisel event | | aws.processEvent | Routes to delete or upsert based on deleteOnEvents configuration | | aws.getProjectionById | Queries the projection table and throws NotFoundError if empty | | aws.getProjectionServiceHandler | Wraps a projection query function as a Lambda service handler | | aws.getConfiguration | Reads projection configuration from environment variables |

Read Side — RDS (aws.getRdsProjectionServices)

The DynamoDB read side above upserts the reduced aggregate as a single row. RDS projections are typed and multi-table, so the SDK provides a transactional projector harness instead: it owns the SQS handler, the stethoscope retry/DLQ wrapping, and one Data-API transaction per event — your bounded context supplies a project(event, state, tx) function that performs the typed multi-table writes. getById/snapshot are owned by the bounded context (they compose multiple tables over @othree.io/awsome's rdsData wrapper) and are intentionally not provided by the SDK for RDS.

import { aws } from '@othree.io/chisel-sdk'
import { rdsData } from '@othree.io/awsome'
import { Optional } from '@othree.io/optional'

// The bounded context implements this. The harness opens the transaction,
// commits on success, and rolls back (then lets stethoscope retry) if you
// return an empty/errored Optional or throw. event.eventId and
// event.metadata?.client_request_id are the idempotency keys — write idempotent
// SQL (INSERT ... ON CONFLICT) so at-least-once re-delivery is safe.
const project = async (
    event: TriggeredEvent<RestaurantEvent>,
    state: Restaurant,
    tx: rdsData.Transaction,
): Promise<Optional<Restaurant>> => {
    await tx.query(
        `INSERT INTO restaurant.restaurants (id, name, market_id)
         VALUES (:id, :name, :market_id)
         ON CONFLICT (id) DO UPDATE SET name = :name`,
        { id: state.id, name: state.name, market_id: state.marketId },
    )
    return Optional(state)
}

const projection = aws.getRdsProjectionServices<Restaurant, RestaurantEvent>({
    boundedContext: 'Restaurant',
    now: () => Date.now(),
    project,
})

// Lambda export — only the SQS consumer; getById is bounded-context-owned.
export const updateProjection = projection.sqs.updateProjection

Environment variables (read automatically):

| Variable | Description | |----------|-------------| | DATA_CLUSTER_ARN | Aurora cluster ARN (from the 00.04 SSM data/cluster-arn export) | | DATA_SECRET_ARN | Cluster credentials secret ARN (data/secret-arn) | | DATA_DATABASE | Database name (data/database) | | PROJECTION_SCHEMA | Per-context Postgres schema (e.g. restaurant) — read by your project SQL | | PROJECTION_DLQ | Application DLQ URL | | LOG_LEVEL | Logging level (default: info) |

The harness makes no single-table / single-PK / state-blob assumption; every write goes through the injected tx.query (the @othree.io/awsome rdsData primitive), which binds values as typed parameters — never interpolated SQL.

Infrastructure (iac.createChiselStacks)

Creates all CDK stacks for a Chisel microservice in a single call.

import { App } from 'aws-cdk-lib'
import { iac } from '@othree.io/chisel-sdk'

const app = new App()

const stacks = iac.createChiselStacks(app, {
    boundedContext: 'Orders',
    version: 'v1',
    projectMetadata: {
        project: 'my-project',
        environment: 'prod',
        owner: 'team-a',
    },
    context: {
        project: { env: 'prod', naming: { prefix: 'myapp' } },
        lambda: { functions: [] },
        chisel: { numberOfShards: 3 },
    },
    writeSide: {
        codePath: 'dist/write',
    },
    readSide: {
        codePath: 'dist/read',
        deleteOnEvents: ['OrderDeleted'],
    },
})

The context is validated against ChiselServiceContextConstraints at runtime using Zod.

Generated Stacks

| Stack | Description | Output | |-------|-------------|--------| | EventBus | FIFO SNS topics (one per shard) | stacks.topics.stack, stacks.topics.topics | | WritePersistence | DynamoDB events table | stacks.writeSide.persistence.eventsTable | | WriteServices | Command handler and get-state Lambda functions | stacks.writeSide.services.handleCommandArn, stacks.writeSide.services.getStateArn | | TopicSubscribers | SQS queues subscribed to SNS topics with DLQ and redrive | stacks.readSide.subscribers.eventQueues, stacks.readSide.subscribers.dlq | | ProjectionPersistence | DynamoDB projection table with optional GSIs | stacks.readSide.persistence.projectionTable | | ReadServices | Update-projection and get-by-id Lambda functions | stacks.readSide.servicesStack.stack |

The read side is optional. Omit readSide from the input to create a write-only service.

When readSide.store === 'RDS', ProjectionPersistence is skipped (no DynamoDB table) and ReadServices contains only the SQS projector Lambda (no get-by-id Lambda) with rds-data + Secrets-Manager grants instead of table grants.

Context Configuration

The ChiselServiceContext controls stack behavior:

type ChiselServiceContext = Readonly<{
    project: {
        env: string                    // Environment name (used as resource suffix)
        naming?: { prefix?: string }   // Optional resource name prefix
    }
    lambda: {
        functions: Array<{
            name: string
            reservedConcurrentExecutions?: number
        }>
    }
    chisel: {
        numberOfShards: number         // Number of FIFO SNS topics
    }
}>

Read Side Options

readSide is a discriminated union on store. A missing store defaults to 'Dynamo' (backward compatible).

type ReadSideInput = DynamoReadSideInput | RdsReadSideInput

type DynamoReadSideInput = Readonly<{
    store?: 'Dynamo'
    codePath: string                          // Lambda code path
    tablePk?: string                          // Projection table PK name (default: 'id')
    globalSecondaryIndexes?: Array<GSIInput>  // Optional GSIs
    deleteOnEvents: Array<string>             // Event types that trigger row deletion
}>

type RdsReadSideInput = Readonly<{
    store: 'RDS'
    codePath: string                          // Lambda code path
    clusterArn: string                        // Aurora cluster ARN (SSM data/cluster-arn)
    secretArn: string                         // Credentials secret ARN (SSM data/secret-arn)
    database: string                          // Database name (SSM data/database)
    schema: string                            // Per-context Postgres schema, e.g. 'restaurant'
}>

For the RDS variant, createChiselStacks provisions the projector Lambda on the SQS event source and grants rds-data:* (scoped to the cluster ARN) plus secretsmanager:GetSecretValue (scoped to the secret ARN). It does not create the Aurora cluster/VPC, define tables, run migrations, or create a projection-persistence stack or a get-by-id Lambda — those are owned externally (the cluster by infrastructure, the tables/queries by the bounded context). With the default aws/secretsmanager KMS key no extra grant is needed; a customer-managed key would require an additional kms:Decrypt grant (not wired at MVP).

const stacks = iac.createChiselStacks(app, {
    boundedContext: 'Restaurant',
    version: 'v1',
    projectMetadata: { project: 'chinchilete', environment: 'dev', owner: 'platform' },
    context: {
        project: { env: 'dev' },
        lambda: { functions: [] },
        chisel: { numberOfShards: 3 },
    },
    writeSide: { codePath: 'dist/write' },
    readSide: {
        store: 'RDS',
        codePath: 'dist/read',
        clusterArn: process.env.DATA_CLUSTER_ARN!,
        secretArn: process.env.DATA_SECRET_ARN!,
        database: 'chinchilete',
        schema: 'restaurant',
    },
})

// On the RDS path stacks.readSide.persistence is undefined (no projection table).

Peer Dependencies

{
    "@othree.io/auditor": "^5.0.0",
    "@othree.io/awsome": "^5.0.0",
    "@othree.io/cdk": "^6.0.0",
    "@othree.io/chisel": "^7.0.0",
    "@othree.io/chisel-aws": "^7.0.0",
    "@othree.io/excuses": "^2.0.0",
    "@othree.io/journal": "^3.0.0",
    "@othree.io/stethoscope": "^5.0.0",
    "aws-cdk-lib": "^2.239.0",
    "aws-lambda": "^1.0.7",
    "aws-xray-sdk-core": "^3.12.0",
    "uuid": "^13.0.0"
}

Development

npm install
npm run build    # Dual CJS/ESM output
npm test         # Vitest with coverage

License

ISC