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

better-cf

v2.0.0

Published

Functional, type-safe Cloudflare SDK suite for Durable Objects and Queues

Readme

better-cf

Opininated Functional, type-safe Cloudflare SDKs for Durable Objects and Queues.

Canonical Imports

  • Primary next-gen surface: better-cf/durable-object
  • Legacy queue surface: better-cf/queue
  • Testing helpers: better-cf/testing
  • CLI binary: better-cf

Quickstart

npm i better-cf zod
npm i -D wrangler @cloudflare/workers-types typescript
npx better-cf init
npm run dev

Create a new worker:

npx better-cf create my-worker
cd my-worker
npm run dev

Primary Surface: better-cf/durable-object

Use schema.ts as the resource registry, keep runtime behavior in sibling files, and call everything through generated ctx.api.

// better-cf.config.ts
import { createSDK } from 'better-cf/durable-object';

export const sdk = createSDK();
export const defineWorker = sdk.defineWorker;
// src/schema.ts
import { z } from 'zod';
import { sdk } from '../better-cf.config';

export const room = sdk.defineDurableObject({
  name: 'Room',
  key: z.string(),
  version: 1,
  description: 'Chat room state.'
});

export const emailQueue = sdk.defineQueue({
  description: 'Email fanout queue.',
  args: z.object({
    roomId: z.string(),
    to: z.string().email(),
    body: z.string()
  }),
  retry: 3,
  retryDelay: '30s'
});
// src/room.ts
import { z } from 'zod';
import { room } from './schema';

export const sendMessage = room.fn({
  description: 'Append a room message.',
  args: z.object({
    body: z.string(),
    author: z.string()
  }),
  returns: z.object({
    ok: z.literal(true)
  }),
  handler: async ({ storage }, args) => {
    const messages = ((await storage.get('messages')) as unknown[] | undefined) ?? [];
    messages.push(args);
    await storage.put('messages', messages);
    return { ok: true };
  }
});
// src/email-queue.ts
import { emailQueue } from './schema';

export const emailQueueConsumer = emailQueue.message({
  description: 'Deliver one email message.',
  handler: async (ctx, job) => {
    await ctx.api.room.sendMessage(job.roomId, {
      body: job.body,
      author: 'system'
    });
  }
});
// worker.ts
import { defineWorker } from './better-cf.config';

export default defineWorker({
  async fetch(_request, ctx) {
    await ctx.api.room.sendMessage('general', {
      body: 'hello',
      author: 'abhi'
    });

    await ctx.api.emailQueue.send({
      roomId: 'general',
      to: '[email protected]',
      body: 'New message'
    });

    return new Response('ok');
  }
});

What the generator adds:

  • .better-cf/entry.ts with worker, queue, and Durable Object wiring
  • .better-cf/types.d.ts with ctx.api typing and JSDoc
  • Wrangler queue and durable_objects bindings
  • SQLite Durable Object migrations

Legacy Queue Surface

better-cf/queue remains available for the original inline-consumer model.

import { createSDK } from 'better-cf/queue';
import { z } from 'zod';

const { defineQueue } = createSDK();

export const signupQueue = defineQueue({
  args: z.object({ email: z.string().email() }),
  handler: async (ctx, msg) => {
    console.log(ctx.message.id, msg.email);
  }
});

Use this surface for existing projects. New projects should prefer better-cf/durable-object.

Testing

Legacy inline queues:

import { testQueue } from 'better-cf/testing';

const result = await testQueue(signupQueue, {
  env: {},
  message: { email: '[email protected]' }
});

expect(result.acked).toHaveLength(1);

Next-gen external consumers and durable functions:

import { testDurableFunction, testQueueConsumer } from 'better-cf/testing';

await testDurableFunction(sendMessage, {
  env: {},
  args: { body: 'hello', author: 'abhi' }
});

await testQueueConsumer(emailQueue, emailQueueConsumer, {
  env: {},
  api: {
    room: {
      sendMessage: async () => ({ ok: true })
    }
  },
  message: {
    roomId: 'general',
    to: '[email protected]',
    body: 'hello'
  }
});

Core Workflow

better-cf dev continuously:

  1. scans declarations and external registrations
  2. validates config and ownership rules
  3. generates .better-cf/entry.ts and type files
  4. patches Wrangler queues, Durable Objects, and SQLite migrations
  5. infers env types
  6. runs or restarts wrangler dev

One-shot generation:

better-cf generate

Scope

The next-gen surface intentionally stays thin over Cloudflare primitives:

  • typed Durable Object RPC methods, alarms, fetch, init, and WebSocket hibernation hooks
  • typed queue producers and external consumers
  • generated ctx.api clients and Wrangler wiring
  • raw escape hatches for native Durable Object namespace methods when needed

Use native Cloudflare APIs directly when the SDK intentionally does not abstract an edge case yet.

Docs

The full docs live under apps/docs, including the new Durable Object section, coverage matrix, migration guide, and legacy queue docs.