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

@neon/effect

v0.1.0

Published

Effect v4 bindings for the Neon SDK: every API call as an Effect, every paginated list as a Stream, with tagged errors and interruption.

Readme

@neon/effect

Effect v4 bindings for the @neon/sdk ergonomic client (createNeonClient). API calls return Effects, paginated lists return Streams and SDK errors have tags for Effect.catchTag. Interrupting a fiber cancels its HTTP request and readiness polling.

npm install @neon/effect effect

Requires Effect v4.

Quick start

Set NEON_API_KEY and optionally NEON_ORG_ID, then provide layerConfig:

import { Neon, layerConfig } from "@neon/effect";
import { Effect, Stream } from "effect";

const program = Effect.gen(function* () {
  const neon = yield* Neon;
  const projects = yield* neon.projects.list().pipe(
    Stream.take(10),
    Stream.runCollect,
  );
  const project = yield* neon.projects.get({
    projectId: "my-project-id",
  }).pipe(
    Effect.catchTag("NeonNotFoundError", () => Effect.succeed(undefined)),
  );
  return { projects, project };
});

Effect.runPromise(program.pipe(Effect.provide(layerConfig)));

Configuration

Neon is a Context.Service whose value is a NeonEffectClient.

| Export | Behavior | | --- | --- | | layer(config) | Provides Neon from explicit configuration. Reads no environment variables. Fails with NeonClientError on invalid configuration. | | layerConfig | Provides Neon through Effect Config. Reads required NEON_API_KEY with Config.Redacted and optional NEON_ORG_ID as the default organization. Fails with NeonClientError or ConfigError. | | make(config) | Returns a NeonEffectClient directly, without a network request. Throws NeonClientError on invalid configuration. |

NeonEffectConfig accepts the SDK's client configuration, excluding throwOnError: apiKey (string or sync/async function), orgId, baseUrl, fetch, retries, requestTimeoutMs, waitForReadiness and wait.

Explicit configuration for the program above:

import { layer, make } from "@neon/effect";

const apiKey = process.env.NEON_API_KEY;
if (!apiKey) throw new Error("NEON_API_KEY is required");

const config = { apiKey, requestTimeoutMs: 10_000 };

Effect.runPromise(program.pipe(Effect.provide(layer(config))));

const neon = make(config);
await Effect.runPromise(
  neon.projects.get({ projectId: "my-project-id" }),
);

An empty apiKey or invalid retries or requestTimeoutMs fails configuration validation. @neon/sdk is installed as a dependency.

Methods and pagination

Method names and named parameters follow the SDK API reference. Each method accepts its SDK per-call options as the last argument, excluding signal and throwOnError.

neon.projects.list(undefined, { requestTimeoutMs: 5_000 });
neon.user.me({ requestTimeoutMs: 5_000 });
neon.branches.create(
  { projectId, name: "preview" },
  { waitForReadiness: true },
);
neon.operations.waitFor(
  { operations },
  { timeoutMs: 30_000, pollIntervalMs: 250 },
);

These paginated methods return Stream<Item, NeonEffectError>:

  • projects.list, projects.members.list
  • branches.list, operations.list
  • functions.list, functions.customDomains.list
  • logs.query
  • consumption.perProject, consumption.perProjectV2, consumption.perBranchV2

Every other method returns Effect<T, NeonEffectError>, including array results such as regions.list and postgres.roles.list. Successful Effects yield the SDK resource directly.

Retries for 423, 429 and 503 (including Retry-After handling), readiness polling, createAndConnect and connection-string resolution run through the SDK unchanged.

Errors

NeonEffectError is the union of the exported tagged error classes below. Each class's _tag matches its name. Every error has message and cause, the original @neon/sdk error.

| Class / _tag | Fields | | --- | --- | | NeonApiError | status, code, requestId, response, body | | NeonNotFoundError | Same HTTP fields; 404 | | NeonAuthError | Same HTTP fields; 401/403 | | NeonRateLimitError | Same HTTP fields; 429 after retries | | NeonOperationError | operationId, status | | NeonRequestTimeoutError | timeoutMs | | NeonWaitTimeoutError | timeoutMs, operations | | NeonNetworkError | reason | | NeonClientError | message, cause |

A wait timeout means the mutation was accepted. Continue waiting on its outstanding operations:

const createBranch = Effect.gen(function* () {
  const neon = yield* Neon;
  return yield* neon.branches.create(
    { projectId: "my-project-id", name: "preview" },
    { wait: { timeoutMs: 30_000 } },
  ).pipe(
    Effect.catchTag("NeonWaitTimeoutError", (error) =>
      neon.operations.waitFor({ operations: error.operations }),
    ),
  );
});

Effect.runPromise(createBranch.pipe(Effect.provide(layerConfig)));

The recovery path returns void when waiting completes. Exceptions outside the SDK error hierarchy become Effect defects. Cancellation interrupts the fiber; the SDK's aborted error kind has no tagged counterpart.

Cancellation and deadlines

Interruption through Effect.timeout, Fiber.interrupt, Effect.race or scope closure for scoped fibers aborts the in-flight request and stops readiness polling. Each Stream page uses the signal of the Effect that pulls it.

requestTimeoutMs bounds each HTTP request and its retries. For a Stream, that budget applies to each page. The SDK's .all() instead uses one deadline for the entire walk. To bound whole-Stream consumption, apply Effect.timeout:

const projects = Effect.gen(function* () {
  const neon = yield* Neon;
  return yield* neon.projects
    .list(undefined, { requestTimeoutMs: 5_000 })
    .pipe(
      Stream.runCollect,
      Effect.timeout("30 seconds"),
    );
});

Effect.runPromise(projects.pipe(Effect.provide(layerConfig)));

Development

From the repository root:

pnpm --filter @neon/effect build
pnpm --filter @neon/effect test
pnpm --filter @neon/effect test:e2e

Live e2e tests need credentials in the root .env for a dedicated throwaway organization. See CONTRIBUTING.md.

License

Apache-2.0