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

@geenius/adapters

v2.3.0

Published

Typed adapter contracts for the Geenius ecosystem. The current package surface is a contract layer for infrastructure domains and launch DB backends: interfaces, Zod schemas, typed errors, SDK-free DB binding helpers, memory/local dev adapters, and retain

Readme

@geenius/adapters

Typed adapter contracts for the Geenius ecosystem. The current package surface is a contract layer for infrastructure domains and launch DB backends: interfaces, Zod schemas, typed errors, SDK-free DB binding helpers, memory/local dev adapters, and retained UI compatibility subpaths.

Install

pnpm add @geenius/adapters

Install only the peers for the subpaths you import:

| Subpath | Styling/runtime | Required peers | |---|---|---| | @geenius/adapters | Framework-agnostic contracts | None | | @geenius/adapters/storage | Object/blob storage contract | None | | @geenius/adapters/cache | Key/value cache contract | None | | @geenius/adapters/queue | Job queue and scheduled-work contract | None | | @geenius/adapters/events | Pub/sub and webhook contract | None | | @geenius/adapters/auth | Session and identity-provider contract | None | | @geenius/adapters/payments | Payment-provider contract | None | | @geenius/adapters/deploy | Deployment-platform contract | None | | @geenius/adapters/react | React, Tailwind CSS v4 | react, react-dom | | @geenius/adapters/react-css | React, Vanilla CSS | react, react-dom | | @geenius/adapters/react-shadcn | React, shadcn/Radix-compatible bridge | react, react-dom | | @geenius/adapters/react-ant | React, Ant Design bridge | react, react-dom, antd, @ant-design/icons | | @geenius/adapters/react-chakra | React, Chakra-compatible bridge | react, react-dom | | @geenius/adapters/react-mui | React, MUI-compatible bridge | react, react-dom | | @geenius/adapters/react-mantine | React, Mantine-compatible bridge | react, react-dom | | @geenius/adapters/react-heroui | React, HeroUI-compatible bridge | react, react-dom | | @geenius/adapters/react-daisyui | React, DaisyUI-compatible bridge | react, react-dom | | @geenius/adapters/react-native | React Native bridge | react, react-native | | @geenius/adapters/solidjs | SolidJS, Tailwind CSS v4 | solid-js | | @geenius/adapters/solidjs-css | SolidJS, Vanilla CSS | solid-js | | @geenius/adapters/solidjs-ark | SolidJS, Ark-compatible bridge | solid-js | | @geenius/adapters/solidjs-kobalte | SolidJS, Kobalte-compatible bridge | solid-js | | @geenius/adapters/solidjs-solidui | SolidJS, SolidUI-compatible bridge | solid-js | | @geenius/adapters/convex | Convex DB binding helpers | convex plus the downstream @geenius/db Convex adapter you bind | | @geenius/adapters/neon | Neon DB binding helpers | The downstream @geenius/db Neon adapter you bind | | @geenius/adapters/cloudflareKV | Cloudflare Workers KV binding helpers | A Workers KV namespace | | @geenius/adapters/memory | In-process DB adapter | None |

Quick Start

Use domain subpaths when a consumer only needs a contract:

import type {
  AuthAdapter,
  AuthProviderAdapter,
} from '@geenius/adapters/auth'
import type { CacheAdapter } from '@geenius/adapters/cache'
import type { DeployAdapter } from '@geenius/adapters/deploy'
import type {
  EventEnvelope,
  EventPublishInput,
  EventsAdapter,
} from '@geenius/adapters/events'
import type { PaymentsProviderAdapter } from '@geenius/adapters/payments'
import type { QueueAdapter } from '@geenius/adapters/queue'
import type { AdapterHealth, StorageAdapter } from '@geenius/adapters/storage'
import {
  AuthContractError,
  AuthIdentitySchema,
} from '@geenius/adapters/auth'
import {
  CacheEntrySchema,
  CacheContractError,
  CacheSetOptionsSchema,
} from '@geenius/adapters/cache'
import {
  DeployContractError,
  DeploymentResultSchema,
  DeploymentTargetSchema,
} from '@geenius/adapters/deploy'
import {
  AdapterHealthSchema as EventsHealthSchema,
  EventEnvelopeSchema,
  EventPublishInputSchema,
  EventSubscribeInputSchema,
  EventsContractError,
  WebhookDeliverySchema,
  WebhookEmitInputSchema,
} from '@geenius/adapters/events'
import {
  PaymentCheckoutInputSchema,
  PaymentCheckoutSessionSchema,
  PaymentPlanContractSchema,
  PaymentSubscriptionContractSchema,
  PaymentsContractError,
} from '@geenius/adapters/payments'
import {
  QueueContractError,
  QueueJobInputSchema,
} from '@geenius/adapters/queue'
import {
  StorageContractError,
  StorageError,
  StorageObjectSchema,
  StorageWriteInputSchema,
} from '@geenius/adapters/storage'

export interface AppAdapters {
  auth: AuthAdapter
  authProvider: AuthProviderAdapter
  cache: CacheAdapter
  deploy: DeployAdapter
  events: EventsAdapter
  paymentsProvider: PaymentsProviderAdapter
  queue: QueueAdapter
  storage: StorageAdapter
}

export function parseCacheEntry(input: unknown) {
  const result = CacheEntrySchema.safeParse(input)

  if (!result.success) {
    throw new CacheContractError('Invalid cache entry', result.error)
  }

  return result.data
}

export async function setCachedValue(
  cache: CacheAdapter,
  key: string,
  value: unknown,
  ttlSeconds: number,
) {
  const result = CacheSetOptionsSchema.safeParse({ ttlSeconds })

  if (!result.success) {
    throw new CacheContractError('Invalid cache set options', result.error)
  }

  await cache.set(key, value, result.data)
}

export async function deployValidatedTarget(
  deploy: DeployAdapter,
  input: unknown,
) {
  const result = DeploymentTargetSchema.safeParse(input)

  if (!result.success) {
    throw new DeployContractError('Invalid deployment target', result.error)
  }

  const deployment = await deploy.deploy(result.data)
  const deploymentResult = DeploymentResultSchema.safeParse(deployment)

  if (!deploymentResult.success) {
    throw new DeployContractError(
      'Invalid deployment result',
      deploymentResult.error,
    )
  }

  return deploymentResult.data
}

export async function putValidatedObject(
  storage: StorageAdapter,
  input: unknown,
) {
  const result = StorageWriteInputSchema.safeParse(input)

  if (!result.success) {
    throw new StorageContractError('Invalid storage write input', result.error)
  }

  const object = await storage.putObject(result.data)
  return StorageObjectSchema.parse(object)
}

export async function listValidatedObjects(storage: StorageAdapter) {
  const objects = await storage.listObjects({ prefix: 'uploads/', limit: 25 })
  return objects.map((object) => StorageObjectSchema.parse(object))
}

export async function requireStorageObject(
  storage: StorageAdapter,
  key: string,
) {
  const object = await storage.getObject(key)

  if (!object) {
    throw new StorageContractError(`Missing storage object: ${key}`)
  }

  return StorageObjectSchema.parse(object)
}

export async function deleteStoredObject(storage: StorageAdapter, key: string) {
  await storage.deleteObject(key)
}

export async function requireStorageHealth(
  storage: StorageAdapter,
): Promise<AdapterHealth> {
  const health = await storage.getHealth()

  if (!health.ok) {
    throw new StorageError(
      'Storage provider health check failed',
      'SERVICE_ERROR',
    )
  }

  return health
}

export async function listValidatedPlans(payments: PaymentsProviderAdapter) {
  const plans = await payments.listPlans()

  return plans.map((plan) => {
    const result = PaymentPlanContractSchema.safeParse(plan)

    if (!result.success) {
      throw new PaymentsContractError('Invalid payment plan', result.error)
    }

    return result.data
  })
}

export async function createValidatedCheckout(
  payments: PaymentsProviderAdapter,
  input: unknown,
) {
  const inputResult = PaymentCheckoutInputSchema.safeParse(input)

  if (!inputResult.success) {
    throw new PaymentsContractError('Invalid checkout input', inputResult.error)
  }

  const session = await payments.createCheckout(inputResult.data)
  const result = PaymentCheckoutSessionSchema.safeParse(session)

  if (!result.success) {
    throw new PaymentsContractError('Invalid checkout session', result.error)
  }

  return result.data
}

export async function getValidatedSubscription(
  payments: PaymentsProviderAdapter,
  identityId: string,
) {
  const subscription = await payments.getSubscription(identityId)

  if (!subscription) {
    return null
  }

  const result = PaymentSubscriptionContractSchema.safeParse(subscription)

  if (!result.success) {
    throw new PaymentsContractError('Invalid subscription', result.error)
  }

  return result.data
}

export async function cancelValidatedSubscription(
  payments: PaymentsProviderAdapter,
  identityId: string,
) {
  await payments.cancelSubscription(identityId)
  return getValidatedSubscription(payments, identityId)
}

export function requireFeatureEntitlement(
  payments: PaymentsProviderAdapter,
  identityId: string,
  feature: string,
) {
  return payments.isFeatureEnabled(identityId, feature)
}

export async function submitQueueJob(
  queue: QueueAdapter,
  input: unknown,
  runAt?: string,
) {
  const result = QueueJobInputSchema.safeParse(input)

  if (!result.success) {
    throw new QueueContractError('Invalid queue job input', result.error)
  }

  return runAt
    ? queue.schedule({ ...result.data, runAt })
    : queue.enqueue(result.data)
}

export async function publishValidatedEvent(
  events: EventsAdapter,
  input: unknown,
) {
  const result = EventPublishInputSchema.safeParse(input)

  if (!result.success) {
    throw new EventsContractError('Invalid event publish input', result.error)
  }

  const event = await events.publish(result.data)
  return EventEnvelopeSchema.parse(event)
}

export async function emitValidatedWebhook(
  events: EventsAdapter,
  input: EventPublishInput,
  url: string,
) {
  const event = await publishValidatedEvent(events, input)
  const webhookInput = WebhookEmitInputSchema.parse({ url, event })
  const delivery = await events.emitWebhook(webhookInput)

  return WebhookDeliverySchema.parse(delivery)
}

export async function subscribeToValidatedEvents(
  events: EventsAdapter,
  topic: string,
  handler: (event: EventEnvelope) => void | Promise<void>,
) {
  const subscribeInput = EventSubscribeInputSchema.parse({ topic })
  const subscription = await events.subscribe(subscribeInput, (event) => {
    return handler(EventEnvelopeSchema.parse(event))
  })

  return {
    unsubscribe: () => subscription.unsubscribe(),
  }
}

export async function listValidatedEvents(
  events: EventsAdapter,
  prefix?: string,
) {
  const envelopes = await events.listEvents({ prefix, limit: 25 })
  return envelopes.map((event) => EventEnvelopeSchema.parse(event))
}

export async function requireEventsHealth(events: EventsAdapter) {
  const health = EventsHealthSchema.parse(await events.getHealth())

  if (!health.ok) {
    throw new EventsContractError(health.message ?? 'Events adapter unhealthy')
  }

  return health
}

export async function requireIdentity(
  auth: AuthProviderAdapter,
  identityId: string,
) {
  const identity = await auth.getIdentity(identityId)

  if (!identity) {
    throw new AuthContractError(`Unknown identity: ${identityId}`)
  }

  return AuthIdentitySchema.parse(identity)
}

Create the shared adapter set once, then pass it to the framework provider.

import {
  configureAdapters,
  createConsoleLoggerAdapter,
  createLocalStorageAuthAdapter,
  createLocalStorageDbAdapter,
} from '@geenius/adapters'

configureAdapters({
  db: { provider: 'localStorage' },
  auth: { provider: 'localStorage' },
  logger: { provider: 'console' },
})

export const adapters = {
  db: createLocalStorageDbAdapter(),
  auth: createLocalStorageAuthAdapter(),
  logger: createConsoleLoggerAdapter(),
}

React:

import {
  AdapterList,
  AdapterProvider,
  useAdapterStatuses,
  useDb,
} from '@geenius/adapters/react'
import { adapters } from './adapters'

function Dashboard() {
  const db = useDb()
  const statuses = useAdapterStatuses()

  void db.list('users')

  return (
    <>
      <p>Database adapter: {statuses.db.status}</p>
      <AdapterList />
    </>
  )
}

export function App() {
  return (
    <AdapterProvider adapters={adapters} healthCheck>
      <Dashboard />
    </AdapterProvider>
  )
}

SolidJS:

import {
  AdapterProvider,
  createAuthAdapter,
  createDb,
} from '@geenius/adapters/solidjs'
import { adapters } from './adapters'

function Dashboard() {
  const auth = createAuthAdapter()
  const db = createDb()

  void auth.getSession()
  void db.list('users')

  return null
}

export function App() {
  return (
    <AdapterProvider adapters={adapters} healthCheck>
      <Dashboard />
    </AdapterProvider>
  )
}

For Vanilla CSS variants, import the stylesheet next to the component subpath:

import { AdapterList } from '@geenius/adapters/react-css'
import '@geenius/adapters/react-css/styles.css'

Configuration Patterns

@geenius/adapters exposes configureAdapters() rather than an exported config builder. Keep app-level presets as plain objects and validate them through the package API:

import type { AdapterConfig } from '@geenius/adapters'
import { configureAdapters, LAUNCH_DB_PROVIDERS } from '@geenius/adapters'

const simple: AdapterConfig = {
  db: { provider: 'localStorage' },
  auth: { provider: 'localStorage' },
  logger: { provider: 'console' },
}

const production: AdapterConfig = {
  db: { provider: LAUNCH_DB_PROVIDERS[0] },
  auth: { provider: 'better-auth' },
  payments: { provider: 'stripe' },
  ai: { provider: 'openai' },
  storage: { provider: 'r2' },
  logger: { provider: 'console' },
}

const isDevelopment = true

configureAdapters(isDevelopment ? simple : production)

Use registerResolver(domain, provider, factory) when the concrete provider factory lives in a downstream package such as @geenius/db, @geenius/auth, @geenius/payments, @geenius/ai, or @geenius/file-storage.

Provider Registry

@geenius/adapters owns two related contract surfaces:

  • Infrastructure domain contracts: storage, cache, queue, events, auth, payments, and deploy.
  • Launch DB backend subpaths: convex, neon, cloudflareKV, and memory.

The older root registry for db, auth, payments, ai, storage, admin, and logger remains exported for UI-provider compatibility. New persistence runtime implementations belong in @geenius/db; new concrete domain provider SDKs belong in their owning domain package.

The DB provider registry exports current launch ids and future metadata from constants.ts:

| Scope | Providers | |---|---| | Launch | convex, neon, cloudflareKV, memory | | Future metadata | supabase, mongodb | | Platform-native metadata | launch convex, launch neon, future supabase, future mongodb | | Edge KV metadata | launch cloudflareKV | | In-process metadata | launch memory | | BYOD ORM metadata | drizzle, prisma, kysely |

Use LAUNCH_DB_PROVIDERS, FUTURE_DB_PROVIDERS, ECOSYSTEM_DB_PROVIDERS, PLATFORM_NATIVE_DB_PROVIDERS, BYOD_ORM_DB_PROVIDERS, getEcosystemDbProviderMeta(), getEcosystemDbProvidersByCategory(), and getEcosystemDbProvidersByStage() for setup UIs and scaffold tooling.

Only the launch row maps to v1 public provider subpaths. Future and BYOD ORM entries are root metadata/resolver ids for setup UIs and downstream wiring; a coordinated ecosystem-axis update is still required before any @geenius/adapters/<provider> import path exists for those ids.

The public Cloudflare KV subpath and provider id are @geenius/adapters/cloudflareKV and cloudflareKV.

neon is reserved for the native Neon/serverless PostgreSQL adapter contract. Downstream apps that use Drizzle should bind it behind their Neon adapter module; drizzle is metadata only and is not a current @geenius/adapters DB provider subpath.

Errors

Adapter failures are surfaced as typed error classes:

  • AdapterError
  • DbError
  • AuthError
  • AiError
  • StorageError
  • PaymentsError
  • AdminError
  • AdapterConfigurationError
  • AdapterResolutionError
  • AdapterContextError
  • AdapterInvariantError

Consumers should catch these exported classes instead of vendor-specific exceptions. Domain subpaths also expose SDK-free contract errors for validation boundaries: PaymentsContractError from @geenius/adapters/payments represents invalid payments contract payloads before a concrete provider runs, while PaymentsError represents root/local runtime payment adapter failures. DeployContractError from @geenius/adapters/deploy has the same role for deployment target and deployment result validation.

import {
  AdapterConfigurationError,
  AdapterResolutionError,
  configureAdapters,
  isAdapterError,
  withRetry,
} from '@geenius/adapters'

const db = {
  list: async (_collection: string) => [],
}

function reportConfigurationProblem(message: string) {
  console.warn(message)
}

function reportAdapterProblem(domain: string, code: string) {
  console.warn(`${domain}:${code}`)
}

try {
  await withRetry(() => db.list('users'), { maxAttempts: 3 })
} catch (error) {
  if (error instanceof AdapterResolutionError) {
    configureAdapters({ db: { provider: 'localStorage' } })
  } else if (error instanceof AdapterConfigurationError) {
    reportConfigurationProblem(error.message)
  } else if (isAdapterError(error)) {
    reportAdapterProblem(error.domain, error.code)
  } else {
    throw error
  }
}

Convex, Neon, Cloudflare KV, And Memory

The Convex and Neon subpaths do not import provider SDKs. They validate and bind DbAdapter instances produced downstream, usually by @geenius/db, and expose the same SDK-free domain contract exports as the other backend subpaths. Cloudflare KV stays limited to a Workers KV namespace contract. Memory is the local/dev implementation surface and also implements every new domain contract with localStorage-compatible mocks.

import type { DbAdapter } from '@geenius/adapters'
import { configureAdapters } from '@geenius/adapters'
import { createCloudflareKvDbAdapter } from '@geenius/adapters/cloudflareKV'
import { createMemoryDbAdapter } from '@geenius/adapters/memory'
import { type ConvexProviderContract, withConvexDb } from '@geenius/adapters/convex'
import { type CreateNeonDbAdapterOptions, withNeonDb } from '@geenius/adapters/neon'

interface Env {
  ADAPTERS_KV: KVNamespace
}

declare function createConvexDbAdapter(options: unknown): DbAdapter
declare const neonAdapterFromGeeniusDb: DbAdapter
declare const neonFactoryOptions: CreateNeonDbAdapterOptions
declare const env: Env

const convexOptions = {}

const convexContract: ConvexProviderContract = {
  provider: 'convex',
  stage: 'launch',
  sdk: 'convex',
  schema: 'ConvexDB schema',
  factory: 'createConvexDbAdapter',
  tables: ['users', 'sessions', 'audit_events'],
}
const convexDb = withConvexDb({
  adapter: createConvexDbAdapter(convexOptions),
  provider: 'convex',
  contract: convexContract,
})
const neonDb = withNeonDb({
  adapter: neonAdapterFromGeeniusDb,
  provider: 'neon',
  source: { factoryOptions: neonFactoryOptions },
})
const memoryDb = createMemoryDbAdapter({ environment: 'development' })
const edgeCacheDb = createCloudflareKvDbAdapter({ namespace: env.ADAPTERS_KV })

configureAdapters({
  db: { provider: 'convex' },
})

Storybook Review Apps

The repository retains one review app for each legacy UI variant. For the current contract-layer mission, variants.json marks the five launch review surfaces as in scope: react, react-css, solidjs, solidjs-css, and react-native. The root Storybook build/test scripts intentionally enumerate every retained review app with a storybook entry so backward-compatible UI subpaths keep review coverage, even when their package source has inScope: false.

pnpm run test:storybook:build
pnpm run test:storybook

Contributing Tests

Variant and provider coverage is driven from variants.json. When adding a new launch variant, add its metadata there first, then add the matching packages/<variant>/ source, Storybook app, e2e harness coverage, and bundle budget. Scripts and tests that enumerate variants should import from @geenius/release-toolkit (packageVariants, uiVariants, dbProviderVariants, …); pnpm fan-out uses geenius-release pnpm-filters <task> [--sequential …].

Use these package-level gates before opening a PR:

pnpm test:gauntlet
pnpm test:all
pnpm test:visual --update-snapshots

test:gauntlet is the PR-blocking suite for lint, public package checks, type-check, unit/root contract tests, bundle budgets, supply-chain audit, and license validation. test:all adds Storybook, DB conformance/migrations, Playwright e2e, accessibility, visual regression, performance smoke, and coverage aggregation. Mutation testing runs separately with pnpm test:mutation.

License

FSL-1.1-Apache-2.0 (free-tier) / Proprietary (commercial)