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

@csbc-dev/lambda

v0.2.0

Published

Declarative AWS Lambda invocation components for Web Components. Remote-first, CSBC-shaped, with parent-child streaming projections.

Downloads

483

Readme

@csbc-dev/lambda

Declarative AWS Lambda invocation components for Web Components, shaped by CSBC and wc-bindable-protocol.

Current state

This repository is in alpha implementation.

  • Parent authority tag: <lambda-invoke>
  • Child stream projection tag: <lambda-stream>
  • Buffered invoke: implemented through AwsLambdaProvider
  • Stream invoke: implemented through AWS InvokeWithResponseStream; custom transports can still be injected through streamInvoker
  • Remote invocation: implemented through a fetch-backed browser provider and server-owned Core handler
  • Remote streaming: the remote handler streams chunks to the browser over NDJSON as they arrive (no buffering); a buffered-JSON server still works as a fallback

The design intent is documented in CLAUDE.md and the package contract is fixed in SPEC.md.

Examples

Runnable browser examples for Vanilla DOM, React, Vue, and @wcstack/state live in the examples README.

The Vite examples install the local package from the workspace, and the static @wcstack/state example loads it through @csbc-dev/lambda/auto. All examples are remote-first: they call a server-owned Core at /api/lambda (the example server holds the provider), so AWS credentials never reach the browser.

Install

npm install @csbc-dev/lambda

Browser-side shape

<lambda-invoke id="chat" mode="stream">
	<lambda-stream></lambda-stream>
</lambda-invoke>

<lambda-invoke> owns inputs, commands, and common invocation state. <lambda-stream> is a projection shell that attaches to the nearest parent <lambda-invoke>.

Server-side bootstrap

Use bootstrapLambdaServer() to create a LambdaCore with an AwsLambdaProvider attached.

import { bootstrapLambdaServer } from "@csbc-dev/lambda/server";

const core = bootstrapLambdaServer({
	pinPolicy: {
		pinnedFunctionName: "my-safe-function",
		pinnedQualifier: "live",
	},
});

If you need direct control, create the provider yourself and attach it to a LambdaCore or <lambda-invoke>.

import { AwsLambdaProvider, LambdaCore } from "@csbc-dev/lambda/server";

const provider = new AwsLambdaProvider({
	policy: {
		pinnedFunctionName: "my-safe-function",
		pinnedQualifier: "live",
	},
});

const core = new LambdaCore(undefined, provider);

Remote invocation

Browser components can attach to a server-owned Core through the fetch-backed remote adapter. The server remains responsible for provider setup, pin policy, and AWS credentials.

import { createLambdaRemoteHandler, bootstrapLambdaServer } from "@csbc-dev/lambda/server";

const createCore = () => bootstrapLambdaServer({
	pinPolicy: {
		pinnedFunctionName: "my-safe-function",
		pinnedQualifier: "live",
	},
});

export const handleLambdaRequest = createLambdaRemoteHandler(() => createCore(), {
	authenticate: (request) => request.headers.get("authorization") === `Bearer ${process.env.LAMBDA_PROXY_TOKEN}`,
});

Remote endpoints must be protected by application authentication or by the authenticate hook shown above. The handler validates the remote command shape, but it does not choose an authorization policy for your application.

Pass a Core factory, as shown above, when one endpoint may receive concurrent requests. A single LambdaCore instance intentionally aborts its previous invocation when a new one starts, which is useful for one addressable invocation surface but not for unrelated HTTP requests.

If you intentionally pass a shared Core instance, the handler rejects overlapping requests with 409 and aborts a stuck shared invocation after sharedCoreTimeoutMs (default: 300000) so the endpoint can recover.

On the browser side, attach the custom element to that endpoint.

const invoke = document.querySelector("lambda-invoke");

invoke?.attachRemote("/api/lambda");
invoke!.payload = { name: "Ada" };

await invoke?.invoke();

The browser may still set functionName or qualifier properties for deployments that explicitly allow it, but the server Core's pin policy is authoritative.

In buffered mode the remote provider exchanges a single JSON request/response.

In stream mode the remote handler responds with an NDJSON body (content-type: application/x-ndjson): one JSON object per line — zero or more {"type":"chunk",...} events as the Lambda response streams in, terminated by exactly one {"type":"result",...} or {"type":"error",...} event. The browser provider reads that stream incrementally and projects each chunk as it arrives, so <lambda-stream> updates token-by-token and firstByteLatency reflects real browser-perceived first-byte latency. The Core still owns transport choice — the browser only sees state — so this is an internal transport, not a contract change (SPEC §9.2, ADR 0001).

Backward-compatible fallback. If the endpoint instead returns one buffered JSON response (an older server, or a deployment whose provider does not stream), the browser provider detects the non-NDJSON content type and replays the returned chunks after completion. In that fallback path every chunk — including the first — arrives only after the invocation completes, so firstByteLatency is the server-measured value replayed verbatim, not browser-perceived. The public bindable surface is identical either way.

Three ways to attach the remote Core (browser)

| Mechanism | When | How | |---|---|---| | remote-url attribute | Per-element, URL known in markup | <lambda-invoke remote-url="/api/lambda"> — a non-empty value attaches a remote provider; clearing it detaches. Declarative; works with framework attr/ref bindings. | | @csbc-dev/lambda/auto/remoteEnv | Whole page, URL from the environment | import "@csbc-dev/lambda/auto/remoteEnv" — registers the elements and enables env-driven remote mode. Each <lambda-invoke> auto-attaches on connect from process.env.LAMBDA_REMOTE_CORE_URL (build-time) or globalThis.LAMBDA_REMOTE_CORE_URL (runtime, set before the script loads). No URL in markup. | | attachRemote(url) | Imperative / dynamic | el.attachRemote("/api/lambda") (or el.remoteUrl = "..."). |

An explicit remote-url attribute or a prior setProvider() takes precedence over env auto-attach — env only fills the gap. The plain @csbc-dev/lambda/auto entry registers the elements without enabling remote mode.

Note for contributors. The auto/auto/remoteEnv entries (src/auto/*.js) are hand-written side-effect modules shipped verbatim — they are not TypeScript-compiled (the build excludes src/auto). They import the build output at ../../dist/index.js, so they only resolve after npm run build has produced dist/. In the published package and on a CDN such as esm.run, src/auto/ and dist/ sit side by side at the package root, so the relative path resolves there too. prepack runs the build before publishing; local examples document "build the workspace first".

Buffered invoke example

import { AwsLambdaProvider, LambdaCore } from "@csbc-dev/lambda/server";

const core = new LambdaCore();

core.setProvider(new AwsLambdaProvider({
	policy: {
		pinnedFunctionName: "hello-world",
		pinnedQualifier: "prod",
	},
}));

await core.invoke({
	payload: { name: "Ada" },
});

console.log(core.result);

Stream invoke example

The default AWS SDK-backed provider uses Lambda InvokeWithResponseStream when mode is "stream".

import { AwsLambdaProvider, LambdaCore } from "@csbc-dev/lambda/server";

const provider = new AwsLambdaProvider({
	policy: {
		pinnedFunctionName: "chat-stream",
	},
});

const core = new LambdaCore(undefined, provider);
await core.invoke({ mode: "stream", payload: { prompt: "hello" } });

console.log(core.text);

For tests, non-AWS runtimes, or server-proxied stream transports, pass a custom streamInvoker to AwsLambdaProvider.

Pin policy

functionName, qualifier, and logType are security-sensitive. The safe default is to pin them server-side.

core.setPinPolicy({
	pinnedFunctionName: "my-safe-function",
	pinnedQualifier: "live",
	pinnedLogType: "None",
});

The browser should not be allowed to choose arbitrary Lambda targets unless the server explicitly allows it. logType is pinned on the same axis because Tail returns the last 4 KB of the function execution log, which can expose runtime environment details. Open each axis explicitly when a deployment needs client selection:

core.setPinPolicy({
	pinnedFunctionName: "default-function",
	allowFunctionNameOverride: true,
	allowedFunctionNames: ["default-function", "tenant-function"],
	allowQualifierOverride: true,
	allowedQualifiers: ["live", "canary"],
	allowLogTypeOverride: true,
});

When an axis is pinned without its override flag, a client-supplied value for that field is silently ignored rather than rejected: el.functionName = "other" keeps the pinned name, and el.logType = "Tail" keeps the pinned log type. The property and function-name / log-type attributes still exist so the declarative wc-bindable contract is uniform across deployments, but the server pin policy — not the browser — is authoritative. Use allowedFunctionNames / allowedQualifiers (with the matching override flag) when an out-of-policy value should instead surface a LAMBDA_POLICY_DENIED error.

Cancellation

abort() cancels local result delivery for the active invocation and passes an AbortSignal to providers. If the underlying provider or transport honors that signal, the in-flight request may be cancelled there too. The package does not promise that an already accepted Lambda execution stops on the AWS side.

Starting a new invocation also aborts the previous provider signal and prevents late results from overwriting newer state.

Deployment: CSP, CORS, and credentials

Because the package is remote-first, the browser only ever talks to your server-owned Core endpoint over fetch. Plan the network boundary explicitly.

Same-origin is the default and the recommended shape. The examples proxy /api/lambda so browser code stays same-origin. createLambdaRemoteHandler emits no CORS headers of its own — a same-origin deployment needs none, and not emitting them by default avoids accidentally opening the endpoint cross-origin.

Content Security Policy. The remote endpoint is reached with fetch, so it must be allowed by connect-src:

Content-Security-Policy: connect-src 'self';

Use 'self' for the same-origin shape. If the Core endpoint is on another origin, add that exact origin (for example connect-src 'self' https://api.example.com;). The package loads no remote scripts itself, so it adds no script-src requirement beyond your own bundle.

Cross-origin endpoints (CORS). If the Core endpoint is on a different origin than the page, the browser will preflight and require CORS headers. createLambdaRemoteHandler does not add them, so your HTTP layer must:

  • answer OPTIONS preflight for the endpoint path
  • return Access-Control-Allow-Origin for an explicit allowlist of trusted origins (never reflect arbitrary origins, and avoid * on an authenticated endpoint)
  • allow the content-type request header and the POST method
  • send Vary: Origin when the allowed origin is computed per request

The example server in examples/server/server.js shows this pattern with an ALLOWED_ORIGINS allowlist.

Credentials mode. The fetch-backed provider sends requests with the browser default credentials mode and forwards any headers you configure. For a token-authenticated endpoint (the authenticate hook above), pass the token via a header rather than relying on ambient cookies. If you do authenticate with cookies on a cross-origin endpoint, you must additionally set Access-Control-Allow-Credentials: true and a non-wildcard Access-Control-Allow-Origin. AWS credentials always stay server-side and are never part of this exchange.

Error behavior

Transport, configuration, policy, and Lambda function failures are exposed through normalized LambdaError objects on error or streamError.

When AWS reports a Lambda FunctionError, the response payload remains available on result, functionError is populated, and error.code is set to LAMBDA_FUNCTION_ERROR.