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

@composurecdk/custom-resources

v0.9.0

Published

Compose-native AwsCustomResource builder for AWS operations with no CloudFormation resource

Readme

@composurecdk/custom-resources

Compose-native AwsCustomResource builder for ComposureCDK.

Some AWS operations have no CloudFormation resource — they are account-level SDK calls only reachable through CDK's AwsCustomResource. The canonical example is ses:SetActiveReceiptRuleSet: the one call that makes an SES receipt rule set actually receive mail. There is no CFN property for "active rule set".

This package wraps AwsCustomResource as a builder so those calls become first-class compose() citizens — with a precise dependency-ordering seam, Resolvable parameters, and IAM sugar.

When a domain builder already covers the call you need, prefer it — it scopes IAM automatically and reads as intent rather than plumbing (e.g. a future SES .activate()). This builder is for the long tail of one-off SDK calls that don't justify a domain builder.

Usage

import { createAwsCustomResourceBuilder } from "@composurecdk/custom-resources";
import { PhysicalResourceId } from "aws-cdk-lib/custom-resources";
import { ref } from "@composurecdk/core";

const activation = createAwsCustomResourceBuilder()
  .onUpdate({
    service: "SES",
    action: "setActiveReceiptRuleSet",
    // Resolvable params: this call can depend on a sibling builder's output
    parameters: ref<ReceiptRuleSetBuilderResult>("ruleSet").map((r) => ({
      RuleSetName: r.ruleSet.receiptRuleSetName,
    })),
    physicalResourceId: PhysicalResourceId.of("active-rule-set"),
  })
  .onDelete({ service: "SES", action: "setActiveReceiptRuleSet", parameters: {} }) // deactivate
  .dependsOn(ref<ReceiptRuleSetBuilderResult>("ruleSet"))
  .allow(["ses:SetActiveReceiptRuleSet"], ["*"]);

// In a composed system, declare the dependency so ordering is wired:
// compose({ ruleSet, activation }, { activation: ["ruleSet"] })

Ordering: dependsOn(...refs)

compose() decides the order your builders run, but CloudFormation decides the order resources deploy — and it only orders resource B after A when B's template references A (a token) or carries an explicit DependsOn. AwsCustomResource JSON-stringifies its parameters, and tokens buried in that blob frequently don't produce the CFN dependency — which is why raw consumers hand-write activate.node.addDependency(ruleSet).

.dependsOn(ref("ruleSet")) is the explicit, reliable seam: it resolves the named component against the build context and adds a DependsOn to that component's construct(s) — even when the SDK call's parameters are hardcoded strings with no token, and only for the component you name (nothing incidental).

IAM: allow(actions, resources)

.allow(...) is sugar over AwsCustomResourcePolicy.fromStatements. resources is required — an account-level action legitimately needs ["*"], but that broad grant should be written explicitly so it is visible in review:

.allow(["ses:SetActiveReceiptRuleSet"], ["*"]) // account-level action — * is legitimate and visible

For full control, pass any policy via .policy(AwsCustomResourcePolicy.fromSdkCalls(...)) (mutually exclusive with .allow). If you supply your own .role(...), a policy is optional.

onDelete caveat

On stack delete with multiple custom resources, CloudFormation can remove a provider's inline IAM policy before its onDelete handler runs, causing an onDelete SDK call to fail with AccessDenied (aws-cdk#9840). If your onDelete performs an API call that needs the granted permissions, be aware it may lose them mid-delete.

Defaults

createAwsCustomResourceBuilder sets a single, unambiguously safe default (overridable via the fluent API), and deliberately does not invent recommended alarms or other resource-style defaults.

| Property | Default | Rationale | | --------------------- | ------- | ----------------------------------------------------------------------------------------------------------------- | | installLatestAwsSdk | false | Use the SDK bundled with the provider Lambda; installing the latest at deploy time is slow and non-deterministic. |

The default is exported as AWS_CUSTOM_RESOURCE_DEFAULTS for visibility and testing.

Reading response values

The build result exposes the underlying construct as { customResource }, so read-style values are reachable via the standard ref machinery:

ref<AwsCustomResourceBuilderResult>("cr", (r) => r.customResource.getResponseField("Item.Id.S"));