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

@maple-dev/effect-sdk

v0.5.0

Published

Maple observability SDK for Effect applications

Readme

@maple-dev/effect-sdk

OpenTelemetry traces, logs, and metrics for Effect applications, powered by Maple.

Install

npm install @maple-dev/effect-sdk effect

Server

Auto-detects commit SHA and deployment environment from common platform env vars (Railway, Vercel, Cloudflare Pages, Render). Returns a no-op layer when no endpoint is configured, making it safe for local development.

import { Maple } from "@maple-dev/effect-sdk/server"
import { Effect } from "effect"

const TracerLive = Maple.layer({ serviceName: "my-app" })

const program = Effect.log("Hello!").pipe(Effect.withSpan("hello"))

Effect.runPromise(program.pipe(Effect.provide(TracerLive)))

Environment Variables

| Variable | Description | | ------------------- | ------------------------------- | | MAPLE_ENDPOINT | Maple ingest endpoint URL | | MAPLE_INGEST_KEY | Maple ingest key | | MAPLE_ENVIRONMENT | Deployment environment override |

Commit SHA is auto-detected from COMMIT_SHA, RAILWAY_GIT_COMMIT_SHA, VERCEL_GIT_COMMIT_SHA, CF_PAGES_COMMIT_SHA, or RENDER_GIT_COMMIT.

Environment is auto-detected from MAPLE_ENVIRONMENT, RAILWAY_ENVIRONMENT, VERCEL_ENV, or NODE_ENV.

Cloudflare Workers

The Workers preset uses a custom flushable tracer + Effect logger — Workers don't run Node-style background tasks, so spans and logs are buffered in-isolate and drained inside ctx.waitUntil() after each request. Construct once at module scope; flush(env) resolves env lazily on the first call.

import * as MapleCloudflareSDK from "@maple-dev/effect-sdk/cloudflare"
import { Layer } from "effect"
import { HttpRouter } from "effect/unstable/http"

const telemetry = MapleCloudflareSDK.make({
	serviceName: "my-worker",
	// Optional: drop noisy spans before they hit OTLP (prefix match).
	// dropSpanNames: ["McpServer/Notifications."],
})

const handler = HttpRouter.toWebHandler(Routes.pipe(Layer.provideMerge(telemetry.layer)))

export default {
	async fetch(req: Request, env: Env, ctx: ExecutionContext) {
		const res = await handler(req)
		ctx.waitUntil(telemetry.flush(env))
		return res
	},
}

telemetry.layer MUST live in the same runtime as your routes — provide it to the layer composition you hand to HttpRouter.toWebHandler, not a separate per-request runtime, or your spans won't pick up the Tracer reference.

When MAPLE_INGEST_KEY is unset, the SDK runs in no-op mode: buffers are drained so they don't grow across the isolate's lifetime, but no requests are made. After a flush failure, each signal sleeps 60s before retrying so a broken collector doesn't get hammered.

Cloudflare-specific options

| Option | Description | | ----------------- | -------------------------------------------------------------------------------------------------------- | | dropSpanNames | Span names whose prefix matches an entry are dropped before OTLP export (e.g. "McpServer/Notifications.") | | excludeLogSpans | Skip Effect log spans in OTLP log attributes. Default false | | tracesPath | OTLP traces path appended to endpoint. Default /v1/traces | | logsPath | OTLP logs path appended to endpoint. Default /v1/logs |

The same MAPLE_ENDPOINT / MAPLE_INGEST_KEY / MAPLE_ENVIRONMENT env vars apply, read from the Workers env binding.

Client (Browser)

All configuration must be provided programmatically since browsers don't have access to environment variables.

import { Maple } from "@maple-dev/effect-sdk/client"
import { Effect } from "effect"

const TracerLive = Maple.layer({
	serviceName: "my-frontend",
	endpoint: "https://ingest.maple.dev",
	ingestKey: "maple_pk_...",
})

const program = Effect.log("Hello!").pipe(Effect.withSpan("hello"))

Effect.runPromise(program.pipe(Effect.provide(TracerLive)))

Manual flush

Maple.layer (server + client) batches in the background and only exports on a timer, on batch overflow, or when its scope closes — there's no way to force an export. That's a problem in two places: a browser tab dropping the last few seconds of spans on unload, and a short-lived process exiting before the timer fires.

MapleFlush.make() (available from both /server and /client) swaps the background exporter for the same buffer-backed tracer/logger the Cloudflare preset uses, and returns an explicit flush():

export interface FlushableTelemetry {
	readonly layer: Layer.Layer<never>
	readonly flush: () => Promise<void> // drain buffers → POST now (never rejects)
	readonly dispose: () => Promise<void> // stop the auto-flush timer/listeners + final flush
}

Both presets run a background auto-flush every 5s by default (configurable via autoFlushInterval, or false to flush purely on demand), so it's a safe drop-in for Maple.layer with manual flush layered on top.

Limitation: the flushable presets export traces + logs only — no metrics (unlike Maple.layer's Otlp.layerJson).

Server / Node

import { MapleFlush } from "@maple-dev/effect-sdk/server"

const telemetry = MapleFlush.make({ serviceName: "my-app" }) // same env auto-detect as Maple.layer

// ...provide telemetry.layer to your runtime...
await telemetry.flush() // force an export at a checkpoint
await telemetry.dispose() // before exit: stop the timer + final flush

Client / Browser

import { MapleFlush } from "@maple-dev/effect-sdk/client"

const telemetry = MapleFlush.make({
	serviceName: "my-frontend",
	endpoint: "https://ingest.maple.dev",
	ingestKey: "maple_pk_...",
	// flushOnUnload: true (default) registers pagehide / visibilitychange→hidden handlers
})
// telemetry.layer keeps the replay-session trace linking from Maple.layer.

By default the client preset flushes on pagehide and visibilitychange→hidden so the tail of a session isn't lost when the tab goes away. Flush uses fetch(url, { keepalive: true }), not navigator.sendBeacon: Maple's ingest authenticates via the Authorization header (no query-param auth) and sendBeacon can't set headers, so it would 401 whenever an ingest key is set. keepalive carries the header and still survives unload for small bodies.

Configuration

Both server and client layers accept these options:

| Option | Required | Description | | ----------------------- | --------------------------------------- | ---------------------------------- | | serviceName | Yes | Service name reported in telemetry | | endpoint | Server: env or config, Client: required | Maple ingest endpoint URL | | ingestKey | No | Maple ingest key | | serviceVersion | No | Override auto-detected commit SHA | | environment | No | Override auto-detected environment | | attributes | No | Additional resource attributes | | maxBatchSize | No | Max batch size for export | | tracerExportInterval | No | Trace export interval | | loggerExportInterval | No | Log export interval | | metricsExportInterval | No | Metrics export interval | | shutdownTimeout | No | Graceful shutdown timeout |

License

MIT