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

@capixjs/transport-queue

v1.0.0

Published

Message queue transport for Capix (BullMQ + in-memory)

Readme

@capixjs/transport-queue

Queue transport for Capix. Process background jobs by routing queue messages to capability resolvers — no HTTP, no WebSocket, just workers consuming from a queue adapter.

Install

npm install @capixjs/core @capixjs/transport-queue zod

Usage

import { createServer } from '@capixjs/core';
import { queueTransport, MemoryQueueAdapter, createQueueClient } from '@capixjs/transport-queue';
import { buildContext, capabilities } from './capabilities.js';

const adapter  = new MemoryQueueAdapter();
const jobQueue = createQueueClient(adapter, 'jobs');

createServer({
  context: buildContext,
  capabilities,
  transports: [
    queueTransport({ queues: ['jobs'], adapter }),
  ],
}).start();

// Enqueue a job from anywhere in your application:
await jobQueue.enqueue('jobs.processOrder', { orderId: '123' });

Adapters

MemoryQueueAdapter

In-process queue, suitable for development and testing. Jobs are not persisted — they are lost if the process restarts.

import { MemoryQueueAdapter } from '@capixjs/transport-queue';
const adapter = new MemoryQueueAdapter();

BullMQAdapter (Redis)

Ships with the package; requires bullmq and ioredis at runtime:

import { BullMQAdapter } from '@capixjs/transport-queue';

const adapter = new BullMQAdapter({
  connection: { host: 'localhost', port: 6379 },
  concurrency: 10,
});

SqsQueueAdapter (Amazon SQS)

Ships with the package; pass an @aws-sdk/client-sqs aggregated client (nothing is bundled):

import { SQS } from '@aws-sdk/client-sqs';
import { SqsQueueAdapter } from '@capixjs/transport-queue';

const adapter = new SqsQueueAdapter({
  client: new SQS({ region: 'eu-west-1' }),
  queueUrls: { jobs: process.env.JOBS_QUEUE_URL! },
  onResult: (msg, result) => {
    if (!result.ok) console.error(`job ${msg.id} failed:`, result.error);
  },
});

Semantics:

  • Success → the message is deleted.
  • Failed result (validation, guard, resolver error) → the message is not deleted; SQS redelivers it after the visibility timeout, and your queue's redrive policy / dead-letter queue caps the retries.
  • Unparseable body → deleted and reported via onError — a poison message would otherwise redeliver forever.
  • FIFO queues (.fifo URLs) automatically get MessageGroupId (the capability name) and MessageDeduplicationId (the message id).
  • stop() drains in-flight handlers, then leaves anything the long poll delivers late for redelivery.

Custom adapters

Implement the QueueAdapter interface to connect to Faktory, NATS, or any queue system:

import type { QueueAdapter, QueueMessage } from '@capixjs/transport-queue';

class BullMQAdapter implements QueueAdapter {
  async start(queue: string, handler: (msg: QueueMessage) => Promise<unknown>): Promise<void> {
    // Subscribe to the BullMQ queue and call handler for each job
  }
  async enqueue(queue: string, msg: QueueMessage): Promise<void> {
    // Add job to BullMQ queue
  }
  async stop(): Promise<void> {
    // Drain and close connections
  }
}

createQueueClient

Creates a typed client for enqueueing jobs:

const jobQueue = createQueueClient(adapter, 'jobs');

// Enqueue with capability name and input
const jobId = await jobQueue.enqueue('jobs.sendEmail', { to: '[email protected]' });

Queue transport vs HTTP

The queue transport invokes capabilities through the same execution engine as REST and WebSocket — guards run, input is validated, context is built. The key difference: the caller is the queue adapter, not an HTTP client.

// jobs-only capabilities never exposed over HTTP
createServer({
  context: buildContext,
  transports: [
    restTransport({ port: 3000, capabilities: publicCaps }),
    queueTransport({ queues: ['jobs'], adapter, capabilities: jobCaps }),
  ],
});

Options

| Option | Type | Description | |--------|------|-------------| | queues | string[] | Queue names to listen on | | adapter | QueueAdapter | Queue backend implementation | | capabilities | GroupTree | Per-transport capability registry (optional) |

Message format

The wire format is the QueueMessage type. Any system that can enqueue a JSON object to your queue can trigger Capix capabilities:

type QueueMessage = {
  capability: string;  // dot-path, e.g. 'jobs.processOrder'
  input:      unknown; // passed to the capability's input validator
};

Security note

The queue transport does not enforce authentication by default. Jobs arrive with a minimal context (just requestId) — there is no Authorization header. Guards using ctx.user will always see null for queue-originated jobs unless you explicitly populate the user field in your context builder by reading from the job input or a service account.

For queue jobs that should run with service-account privileges, pattern the context builder to check for a job-specific header or trust flag:

const buildContext = defineContext(async (req) => ({
  requestId: crypto.randomUUID(),
  user: req.headers['x-service-account'] === process.env.QUEUE_SECRET
    ? SERVICE_ACCOUNT
    : await verifyJwt(req.headers.authorization),
}));

License

MIT