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

@velajs/vela

v1.5.1

Published

NestJS-compatible framework for edge runtimes, powered by Hono

Readme

@velajs/vela

npm version CI License: MIT

NestJS-compatible framework for edge runtimes, powered by Hono.

Install

pnpm add @velajs/vela

Quick Start

import { VelaFactory, Controller, Get, Module, Injectable } from '@velajs/vela';

@Injectable()
class AppService {
  getHello() {
    return { message: 'Hello from the edge!' };
  }
}

@Controller('/app')
class AppController {
  constructor(private appService: AppService) {}

  @Get('/')
  hello() {
    return this.appService.getHello();
  }
}

@Module({
  controllers: [AppController],
  providers: [AppService],
})
class AppModule {}

const app = await VelaFactory.create(AppModule);
export default app; // Works on Cloudflare Workers, Deno, Bun, etc.

Features

  • Decorator-based controllers@Controller, @Get, @Post, @Put, @Patch, @Delete
  • Dependency injection@Injectable, @Inject, InjectionToken, singleton/transient/request scopes
  • Modules@Module with imports, exports, controllers, providers
  • Guards@UseGuards with CanActivate interface
  • Pipes@UsePipes, built-in ParseIntPipe, ParseBoolPipe, ZodValidationPipe, etc.
  • Interceptors@UseInterceptors with NestInterceptor interface
  • Exception filters@UseFilters, @Catch, built-in HTTP exceptions
  • Middleware@UseMiddleware for Hono-native middleware
  • Custom metadata@SetMetadata + Reflector
  • Custom param decoratorscreateParamDecorator
  • Route versioning@Controller({ version: '1' }) + @Version('2')
  • Global prefixapp.setGlobalPrefix('/api')
  • Lifecycle hooksOnModuleInit, OnApplicationBootstrap, OnModuleDestroy
  • CRUD integration — Optional @velajs/crud package

Edge Runtime Compatibility

Vela runs on any runtime that supports the Web Standards API:

  • Cloudflare Workers
  • Deno Deploy
  • Bun
  • Node.js 20+
  • Vercel Edge Functions

No Node.js-specific APIs (node:fs, Buffer, process) are used.

Edge-safe contract

The main export (@velajs/vela) is edge-safe by contract — no node:* imports, no Buffer, no process, no setInterval, no Bun.serve. This is enforced in CI by src/__tests__/edge-runtime-audit.test.ts, which fails the build if any file under src/ references a forbidden API.

One subpath, @velajs/vela/schedule-node, is an opt-in Node/Bun adapter for setInterval-based job execution. It uses runtime-specific APIs by design and is excluded from the edge-runtime audit. Edge runtimes (Cloudflare Workers, Deno Deploy, Vercel Edge) should not import it — use platform cron triggers instead (e.g., @velajs/cloudflare ≥ 0.2.0 dispatches @Cron jobs via the Workers scheduled() handler).

// Node / Bun only — opt-in
import { ScheduleNodeModule } from '@velajs/vela/schedule-node';

The other subpaths (@velajs/vela/internal, @velajs/vela/streaming) follow the main export's edge-safe contract.

Dynamic modules

Configurable modules use forRoot (sync) and forRootAsync (DI-resolved):

@Module({
  imports: [
    CacheModule.forRoot({ ttl: 60 }),
    HttpModule.forRoot({ baseURL: 'https://api.example.com' }),
    ConfigModule.forRootAsync({
      useFactory: async (loader: ConfigLoader) => loader.load(),
      inject: [ConfigLoader],
    }),
  ],
})
class AppModule {}

Identity model

Each DynamicModule has an optional key?: string that discriminates one instance from another. First-party modules derive key: stableHash(options) automatically inside forRoot — so the same options always dedup, and distinct options register as distinct instances:

// Same options → dedup (one CacheModule instance, ttl: 60)
imports: [
  CacheModule.forRoot({ ttl: 60 }),
  CacheModule.forRoot({ ttl: 60 }),
]

// Different options → two distinct instances coexist
imports: [
  CacheModule.forRoot({ ttl: 60 }),
  CacheModule.forRoot({ ttl: 120 }),
]

When a consumer module imports two instances both exporting the same logical token, the resolver throws MultipleProvidersFoundError with both candidate ids — resolve the ambiguity by importing only one, or use a per-instance accessor exposed by the module. Most apps with a single instance never hit this.

Custom modules can use the same pattern via the public helpers:

import { defineDynamicModule, stableHash } from '@velajs/vela';

class MyModule {
  static forRoot(options: MyOptions): DynamicModule {
    return defineDynamicModule({
      module: MyModule,
      key: stableHash(options),    // or pass an explicit key
      providers: [/* ... */],
      exports: [/* ... */],
    });
  }
}

forRootAsync callers should pass key explicitly when the same module needs multiple async instances — factories aren't structurally hashable.

Companion packages

| Package | Purpose | |---|---| | @velajs/cloudflare | Cloudflare Workers adapter — typed services for KV, D1, R2, Queues, DO, AI, Vectorize, Hyperdrive | | @velajs/crud | NestJS-style CRUD controllers on top of hono-crud | | @velajs/testing | Test.createTestingModule() with overrideProvider/Guard/Pipe/Interceptor/Filter |

pnpm add @velajs/testing -D
pnpm add @velajs/cloudflare @cloudflare/workers-types
pnpm add @velajs/crud hono-crud @hono/zod-openapi zod

/internal subpath (for plugin authors)

Framework primitives — MetadataRegistry, Container, RouteManager, ModuleLoader, ComponentManager, VelaApplication, bindAppProviders, APP_* tokens — are exposed at @velajs/vela/internal. This is the stable target for plugin packages that need to reach below the public API.

import { MetadataRegistry, Container } from '@velajs/vela/internal';

The public root barrel still exports MetadataRegistry (used by tests for clear() between cases). Everything else lives at /internal only.

License

MIT