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

sls-ts

v0.1.1

Published

Type-safe infrastructure configuration for Serverless Framework

Downloads

6

Readme

sls-ts

Type-safe infrastructure configuration for osls (the maintained Serverless Framework v3 fork). Write your serverless config in TypeScript, get the YAML you already know works.

Why?

You shouldn't have to choose between type safety and a proven deployment tool. The Serverless Framework works. It deploys reliably. It has plugins that actually function. But YAML duplication across multiple apps is a nightmare.

sls-ts gives you type-safe constructs, generates your serverless.yml, and wraps the dev/deploy workflow.

What it does

  • Type-safe constructs for AWS resources (API Gateway, DynamoDB, Cognito, S3, SQS, SNS, SES, WebSocket, EventBridge)
  • Generates serverless.yml from TypeScript config
  • Wraps dev/deploy/remove workflow with a single CLI
  • Generates frontend .env files from your config
  • Works with osls and existing plugins
  • Lets you iterate locally without touching AWS

What it doesn't do

  • Replace osls/Serverless Framework (it uses it)
  • Replace serverless-offline (it depends on it)
  • Abstract away CloudFormation (you still see the resources)
  • Add vendor lock-in (it's just a code generator)

Installation

npm install --save-dev sls-ts
npx sls-ts init   # Installs osls and required plugins

Quick Start

Create infra/config.ts:

import { defineConfig, staticSite, api, dynamoTable, cognitoAuth, sqs, sns, schedule, s3Bucket, ses, websocket, eventBridge } from 'sls-ts';

export default defineConfig({
  app: 'my-app',
  
  auth: cognitoAuth({ 
    emailLogin: true 
  }),
  
  tables: {
    users: dynamoTable({ 
      pk: 'userId',
      sk: 'timestamp'
    }),
  },
  
  api: api({
    routes: {
      'GET /users': 'src/handlers/getUsers.handler',
      'POST /users': 'src/handlers/createUser.handler',
    },
    authorizer: 'cognito',
  }),
  
  sites: {
    admin: staticSite({ 
      path: 'apps/admin', 
      buildOutput: '.dist' 
    }),
    web: staticSite({ 
      path: 'apps/web', 
      buildOutput: '.dist' 
    }),
  },
});

Run locally:

npx sls-ts dev

Deploy:

npx sls-ts deploy --stage staging
npx sls-ts deploy --stage prod

Constructs

staticSite(options)

Generates S3 + CloudFront configuration for static site hosting.

staticSite({
  path: 'apps/web',           // Path to your app
  buildOutput: '.dist',       // Build output directory
  domain: {                   // Optional custom domain
    domain: 'www.example.com',
    certificateArn: 'arn:aws:acm:us-east-1:...',
    hostedZoneId: 'Z1234...',  // Creates Route53 A record
  },
})

Generates:

  • S3 bucket with proper policies
  • CloudFront distribution with OAC
  • ACM certificate attachment
  • Route53 A record (if hostedZoneId provided)
  • Cache policies and output URLs

api(options)

Generates API Gateway HTTP API + Lambda configuration.

api({
  routes: {
    // Simple handler string
    'GET /users': 'src/handlers/getUsers.handler',
    // Or with Lambda config
    'POST /users': {
      handler: 'src/handlers/createUser.handler',
      timeout: 30,
      memorySize: 512,
    },
  },
  authorizer: 'cognito',      // Optional JWT authorizer
  cors: true,                 // Enable CORS
  defaults: {                 // Default Lambda config for all routes
    timeout: 10,
    memorySize: 1024,
  },
  domain: {                   // Optional custom domain
    domain: 'api.example.com',
    certificateArn: 'arn:aws:acm:us-east-1:...',
    hostedZoneId: 'Z1234...',  // Creates Route53 A record
  },
})

Generates:

  • Lambda functions for each route
  • HTTP API with optional JWT authorizer
  • Custom domain with API mapping (if configured)
  • Route53 A record (if hostedZoneId provided)

dynamoTable(options)

Generates DynamoDB table configuration.

dynamoTable({
  pk: 'userId',              // Partition key
  sk: 'timestamp',           // Optional sort key
  gsi: [                     // Optional GSIs
    { pk: 'email', name: 'EmailIndex' }
  ],
  ttl: 'expiresAt',          // TTL attribute (auto-delete expired items)
  stream: 'NEW_AND_OLD_IMAGES', // DynamoDB Streams for triggers
  localSeed: './seed/users.json',  // Optional seed data for local dev
})

Generates:

  • DynamoDB table with PAY_PER_REQUEST billing
  • TTL configuration for automatic item expiration
  • Stream configuration for event-driven triggers
  • IAM permissions for Lambda functions
  • Environment variable with table name
  • Local DynamoDB configuration with seeding

cognitoAuth(options)

Generates Cognito User Pool and Client.

cognitoAuth({
  emailLogin: true,
  selfSignUp: true,           // Allow users to register (default: true)
  emailVerification: true,    // Require email verification (default: true)
  mfa: 'OPTIONAL',            // 'OFF' | 'OPTIONAL' | 'ON' (default: 'OFF')
  passwordPolicy: {
    minLength: 12,
    requireNumbers: true,
    requireSymbols: true,
    requireUppercase: true,
    requireLowercase: true,
  },
})
  • selfSignUp: false - Admin-only account creation (internal tools)
  • mfa: 'ON' - Require MFA for all users (high-security apps)

sqs(options)

Generates SQS queues with Lambda consumers.

sqs({
  handler: 'src/handlers/processJob.handler',  // Consumer function
  batchSize: 10,          // Messages per invocation (default: 10)
  maxBatchingWindow: 5,   // Seconds to wait for batch (default: 0)
  visibilityTimeout: 30,  // Seconds before retry (default: 30)
  dlq: true,              // Enable dead-letter queue
  // dlq: { maxReceiveCount: 5 }  // Custom retry count
  timeout: 30,            // Lambda timeout
  memorySize: 512,        // Lambda memory
})

Generates:

  • SQS queue with configurable settings
  • Dead-letter queue (optional)
  • Lambda consumer with SQS trigger
  • IAM permissions and environment variables (QUEUE_NAME_URL)

sns(options)

Generates SNS topics with optional Lambda subscribers.

sns()                                    // Topic only, no subscriber
sns({ 
  handler: 'src/handlers/notify.handler' // With subscriber
})

Generates:

  • SNS topic
  • Lambda subscriber (optional)
  • IAM permissions and environment variables (TOPIC_NAME_ARN)

schedule(options)

Generates EventBridge scheduled functions.

schedule({
  handler: 'src/handlers/cleanup.handler',
  rate: 'rate(1 hour)',                // OR cron expression
  // cron: 'cron(0 12 * * ? *)',       // Daily at noon UTC
  input: { type: 'full' },             // Optional payload
  enabled: true,                       // Default: true
  timeout: 900,                        // Lambda timeout (up to 15 min)
  memorySize: 2048,                    // Lambda memory
})

s3Bucket(options)

Generates S3 buckets for file storage.

s3Bucket({
  cors: true,              // Enable CORS for browser uploads
  versioning: true,        // Enable versioning
  expiration: 30,          // Auto-delete after N days
  handlers: {
    onCreate: 'src/handlers/onUpload.handler',
    onDelete: 'src/handlers/onDelete.handler',
  },
})

Generates:

  • S3 bucket with public access blocked
  • CORS, versioning, lifecycle rules (optional)
  • Lambda triggers for S3 events (optional)
  • IAM permissions and environment variables (BUCKET_NAME)

ses(options)

Generates SES email identity for transactional emails.

ses({
  domain: 'example.com',
  verifyDomain: true,      // Auto-verify with Route53
  hostedZoneId: 'Z123...',
})

Generates:

  • SES email identity
  • DKIM Route53 records (if verifyDomain + hostedZoneId)
  • IAM permissions and environment variable (SES_FROM_DOMAIN)

websocket(options)

Generates WebSocket API Gateway for real-time features.

websocket({
  routes: {
    $connect: 'src/handlers/connect.handler',
    $disconnect: 'src/handlers/disconnect.handler',
    $default: 'src/handlers/default.handler',
    sendMessage: 'src/handlers/sendMessage.handler',
  },
})

Generates:

  • WebSocket API with route selection
  • Lambda functions for each route
  • IAM permissions for ManageConnections
  • Environment variables (WEBSOCKET_API_ID, WEBSOCKET_ENDPOINT)

eventBridge(options)

Generates EventBridge rules for event-driven architecture.

eventBridge({
  handler: 'src/handlers/onOrderCreated.handler',
  pattern: {
    source: ['my.app'],
    'detail-type': ['order.created'],
    detail: { status: ['confirmed'] },
  },
  bus: 'orders',       // Optional custom bus (default: 'default')
  enabled: true,       // Default: true
})

Generates:

  • Lambda function triggered by events
  • Custom EventBus resource (if not using default)
  • IAM permissions for events:PutEvents

ssm(path, decrypt?)

Reference AWS SSM Parameter Store values for secrets and configuration.

import { ssm } from 'sls-ts';

ssm('/myapp/prod/api-key')        // SecureString, auto-decrypted
ssm('/myapp/prod/config', false)  // Plain String, no decryption

Multi-Environment Configuration

Pass a function to defineConfig to customize configuration per stage:

import { defineConfig, staticSite, api, cognitoAuth } from 'sls-ts';

export default defineConfig((stage) => ({
  app: 'my-app',

  auth: cognitoAuth({
    emailLogin: true,
    passwordPolicy: stage === 'prod'
      ? { minLength: 12, requireSymbols: true }
      : { minLength: 8 },
  }),

  sites: {
    web: staticSite({
      path: 'apps/web',
      buildOutput: '.dist',
      // Custom domain only in prod
      domain: stage === 'prod' ? {
        domain: 'www.example.com',
        certificateArn: 'arn:aws:acm:us-east-1:123456789:certificate/xxx',
      } : undefined,
    }),
  },
}));

Generate for each environment:

npx sls-ts generate --stage dev
npx sls-ts generate --stage staging
npx sls-ts generate --stage prod

Environment Variables & Secrets

Use the env option to define environment variables. Combine with ssm() for secrets:

import { defineConfig, api, ssm } from 'sls-ts';

export default defineConfig((stage) => ({
  app: 'my-app',

  env: {
    NODE_ENV: stage,
    LOG_LEVEL: stage === 'prod' ? 'error' : 'debug',

    // Secrets from SSM Parameter Store
    DATABASE_URL: ssm(`/myapp/${stage}/database-url`),
    API_KEY: ssm(`/myapp/${stage}/api-key`),
    STRIPE_SECRET: ssm(`/myapp/${stage}/stripe-secret`),
  },

  api: api({
    routes: {
      'POST /checkout': 'src/handlers/checkout.handler',
    },
  }),
}));

This generates:

provider:
  environment:
    NODE_ENV: prod
    LOG_LEVEL: error
    DATABASE_URL: ${ssm:/myapp/prod/database-url~true}
    API_KEY: ${ssm:/myapp/prod/api-key~true}
    STRIPE_SECRET: ${ssm:/myapp/prod/stripe-secret~true}

Create your SSM parameters:

aws ssm put-parameter \
  --name "/myapp/prod/database-url" \
  --value "postgres://user:pass@host:5432/db" \
  --type SecureString

aws ssm put-parameter \
  --name "/myapp/prod/api-key" \
  --value "sk_live_..." \
  --type SecureString

osls resolves these at deploy time—secrets never touch your codebase or CI logs.

Custom Domains & SSL

Configure custom domains with ACM certificates and automatic Route53 records:

import { defineConfig, staticSite, api } from 'sls-ts';

// Reusable domain config
const cert = 'arn:aws:acm:us-east-1:123456789:certificate/abc-123';
const hostedZoneId = 'Z1234567890ABC';

export default defineConfig((stage) => ({
  app: 'my-app',

  api: api({
    routes: { 'GET /health': 'src/handlers/health.handler' },
    domain: stage === 'prod' ? {
      domain: 'api.example.com',
      certificateArn: cert,
      hostedZoneId,
    } : undefined,
  }),

  sites: {
    web: staticSite({
      path: 'apps/web',
      buildOutput: '.dist',
      domain: stage === 'prod' ? {
        domain: 'www.example.com',
        certificateArn: cert,
        hostedZoneId,
      } : undefined,
    }),
  },
}));

Prerequisites:

  1. ACM Certificate - Must be in us-east-1 for CloudFront:

    aws acm request-certificate \
      --domain-name "*.example.com" \
      --validation-method DNS \
      --region us-east-1
  2. Route53 Hosted Zone - Get the ID:

    aws route53 list-hosted-zones-by-name \
      --dns-name example.com \
      --query 'HostedZones[0].Id' \
      --output text

When hostedZoneId is provided, sls-ts generates Route53 A records pointing to your CloudFront distribution or API Gateway domain.

Local Development

Generate Environment Files

Generate .env files for your frontend apps:

# Generate .env.local for local development
npx sls-ts env --stage local

# Generate .env.prod with production URLs
npx sls-ts env --stage prod

This creates .env.{stage} files in each site's directory with:

# apps/web/.env.local
VITE_API_URL=http://localhost:3001
VITE_USER_POOL_ID=local_user_pool
VITE_USER_POOL_CLIENT_ID=local_client
VITE_STAGE=local

# Also generates NEXT_PUBLIC_* variants for Next.js

Running Locally

# Start everything with one command
npx sls-ts dev

# This automatically:
# 1. Generates serverless.yml
# 2. Generates .env files for frontends
# 3. Starts serverless-offline

Or add to your package.json:

{
  "scripts": {
    "dev": "sls-ts dev",
    "dev:web": "cd apps/web && npm run dev",
    "deploy:staging": "sls-ts deploy --stage staging",
    "deploy:prod": "sls-ts deploy --stage prod"
  }
}

Run API and frontend together:

# Terminal 1: API
npm run dev

# Terminal 2: Frontend
npm run dev:web

Local DynamoDB with Seed Data

sls-ts automatically configures local DynamoDB when you have tables. Add seed data for realistic local testing:

1. Define tables with seed files:

// infra/config.ts
export default defineConfig({
  app: 'my-app',
  tables: {
    users: dynamoTable({
      pk: 'userId',
      sk: 'createdAt',
      localSeed: './seed/users.json',
    }),
    posts: dynamoTable({
      pk: 'postId',
      localSeed: './seed/posts.json',
    }),
  },
});

2. Create seed files:

// seed/users.json
[
  {
    "userId": "user-1",
    "createdAt": "2024-01-01T00:00:00Z",
    "email": "[email protected]",
    "name": "Alice"
  },
  {
    "userId": "user-2",
    "createdAt": "2024-01-02T00:00:00Z",
    "email": "[email protected]",
    "name": "Bob"
  }
]
// seed/posts.json
[
  {
    "postId": "post-1",
    "authorId": "user-1",
    "title": "Hello World",
    "content": "This is a test post"
  }
]

3. Run locally:

# Install local DynamoDB (first time only)
npx osls dynamodb install

# Start API with seeded database
npx sls-ts dev

The seed data is automatically loaded when DynamoDB Local starts. Tables are created in-memory and reset on each restart.

Local Auth Mocking

For local development, the generated env files use placeholder Cognito values. Your frontend can check VITE_STAGE === 'local' to bypass real auth:

// src/auth.ts
const isLocal = import.meta.env.VITE_STAGE === 'local';

export async function getAuthToken(): Promise<string | null> {
  if (isLocal) {
    // Return a mock JWT for local development
    return 'eyJ...local-mock-token';
  }
  // Real Cognito auth flow
  return await Auth.currentSession().getIdToken().getJwtToken();
}

Plugin Support

sls-ts works with standard osls/Serverless Framework plugins:

  • serverless-offline - Local Lambda execution
  • serverless-dynamodb - Local DynamoDB
  • serverless-s3-sync - S3 deployment
  • serverless-cloudfront-invalidate - Cache invalidation
  • Any other plugin that reads serverless.yml

TypeScript Benefits

// Autocomplete for routes
api({
  routes: {
    'GET /users': 'src/handlers/getUsers.handler', // ← IDE validates path
  }
})

// Type-checked table references
dynamoTable({
  pk: 'userId',  // ← Knows it's a string key
})

// Shared configuration
const commonCacheBehavior = {
  compress: true,
  defaultTTL: 1800,
};

staticSite({ 
  path: 'apps/web',
  cacheBehavior: commonCacheBehavior, // ← Reuse across sites
})

Philosophy

No magic. No abstractions you can't debug.

Every construct generates CloudFormation resources you can inspect. If something breaks, you can see exactly what YAML was generated and why.

This isn't trying to replace AWS CDK or Pulumi. It's a thin layer over the tool you already use, adding types and eliminating duplication.

Why osls?

osls is the maintained open-source fork of Serverless Framework v3. It's a drop-in replacement that:

  • Supports new AWS Lambda runtimes
  • Fixes security vulnerabilities
  • Removes Serverless Dashboard/Enterprise bloat
  • Is actively maintained by the community

Install globally: npm install -g osls

Comparison

| Tool | Type Safety | Local Dev | Deploys With | Abstracts CFN | |------|-------------|-----------|--------------|---------------| | sls-ts | ✅ | ✅ | osls | No | | osls | ❌ | ✅ | Itself | No | | AWS CDK | ✅ | ❌ | CloudFormation | Yes | | SST | ✅ | ❌ (uses real AWS) | Custom | Yes | | Pulumi | ✅ | ❌ | Custom | Yes |

CLI Reference

# Install osls and required plugins
npx sls-ts init

# Local development (generates config + env files + starts serverless offline)
npx sls-ts dev
npx sls-ts dev --stage dev

# Deploy (generates config + deploys to AWS)
npx sls-ts deploy --stage staging
npx sls-ts deploy --stage prod
npx sls-ts deploy --stage prod --profile prod-account

# Remove deployed stack
npx sls-ts remove --stage staging
npx sls-ts remove --stage prod --profile prod-account

# Generate serverless.yml only
npx sls-ts generate
npx sls-ts generate --stage prod
npx sls-ts generate --config ./custom/config.ts
npx sls-ts generate --output ./services/api/serverless.yml

# Generate .env files for frontends (fetches real URLs from deployed stack)
npx sls-ts env --stage local   # Uses localhost
npx sls-ts env --stage prod    # Fetches from CloudFormation outputs

Options:

  • --stage - Deployment stage (default: local for dev, dev for others)
  • --profile - AWS profile to use
  • --config - Path to config file
  • --output - Output path for serverless.yml

Roadmap

  • [x] Core config generator
  • [x] Static site construct
  • [x] API construct
  • [x] DynamoDB construct
  • [x] Cognito construct
  • [x] SSM Parameter Store support
  • [x] Multi-environment configs
  • [x] SQS construct (queues with consumers)
  • [x] SNS construct (pub/sub topics)
  • [x] Schedule construct (EventBridge cron/rate)
  • [x] S3 bucket construct (file uploads)
  • [x] SES construct (transactional email)
  • [x] WebSocket API construct
  • [x] EventBridge construct (event rules)
  • [ ] Step Functions constructs
  • [ ] Custom construct API

Contributing

This project exists because the ecosystem failed to solve a simple problem. PRs welcome, but keep it simple. We're building a code generator, not a framework.

License

MIT

Credits

Built by someone who got tired of duplicating YAML across 5 different serverless.yml files and decided to fix it properly.


Just trying to ship serverless apps without the bullshit.