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

shin-bucket-deployment

v0.10.1

Published

Rust-backed AWS CDK construct for performance-sensitive S3 asset deployment

Readme

ShinBucketDeployment

Rust-backed AWS CDK construct for performance-sensitive S3 asset deployment.

ShinBucketDeployment is a focused alternative to AWS CDK's official BucketDeployment, intended for static assets when you want a purpose-built Rust provider and fewer full-archive extraction costs. It accepts upstream CDK ISource implementations while owning its configuration API directly.

The published package ships prebuilt Rust provider binaries for both Lambda architectures (arm64 and x86_64), so consumers do not need a Rust toolchain.

Quick Start

Install the package in your CDK v2 project:

npm install shin-bucket-deployment

Minimal Example

import { Stack } from "aws-cdk-lib";
import { Bucket } from "aws-cdk-lib/aws-s3";
import { Construct } from "constructs";
import { ShinBucketDeployment, Source } from "shin-bucket-deployment";

export class DemoStack extends Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    const bucket = new Bucket(this, "WebsiteBucket");

    new ShinBucketDeployment(this, "DeployWebsite", {
      sources: [Source.asset("site")],
      destination: { bucket },
    });
  }
}

Migrating from BucketDeployment

Migration usually starts with this import change:

-import { BucketDeployment, Source } from "aws-cdk-lib/aws-s3-deployment";
+import { ShinBucketDeployment, Source } from "shin-bucket-deployment";

See the full property mapping for replaced and unsupported properties.

Why Build This

The official BucketDeployment is a good default for many stacks, but its provider is built around AWS CLI copy/sync orchestration. Shin uses a purpose-built Rust Lambda function and a configuration model that exposes provider identity separately from request-scoped transfer controls.

| Advantage | What changes | | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Leaner runtime | This custom resource provider runs on the Lambda Rust runtime (provided.al2023) rather than the Python runtime used by the upstream provider. Its native provider starts faster and uses less memory on the target workloads. See Benchmark Snapshots. | | Direct AWS SDK operations | S3 copy, upload, and delete work is executed through Rust SDK calls instead of the upstream provider's aws s3 cp, aws s3 sync, and aws s3 rm subprocesses. | | Archive-aware planning | For extracted assets, the provider plans directly from the zip archive instead of extracting the whole archive to a working directory before syncing. | | Invocation-wide memory cap | Central-directory planning and every source archive share a source-memory budget capped at half the provider's actual Lambda memory by default; destination cleanup retains at most manifest metadata plus one S3 page. | | Bounded fail-fast transfers | Transfer concurrency is capped by transfer.maxConcurrency. The first observed failure or panic stops admission and drains outstanding work before cleanup or invalidation can continue. | | Encryption-aware writes | SSE-S3 destinations use the cheap single-part MD5/ETag path; KMS and DSSE destinations store full-object SHA-256 only where encrypted ETags cannot prove content identity. | | Bounded marker replacement | Marker-free entries stream directly. Marker entries use deterministic simultaneous replacement with one exact-length planning pass and a second retryable streaming pass only when upload is required; neither pass retains the complete entry or output. | | Safer destination moves | Opt-in cleanup deploys new content first, infers the previous prefix, and preserves overlapping current namespaces. See Destination Lifecycle. |

Benchmark Snapshots

[!CAUTION] These charts are illustrative, not performance guarantees or guidance for production defaults. See Benchmark for the supporting evidence.

Construct API

Only sources and destination are required. Sources accept any upstream CDK ISource; Shin also provides Source.asset, Source.bucket, Source.data, Source.jsonData, and Source.yamlData helpers.

interface ShinBucketDeploymentProps {
  // Ordered upstream ISource values. Later sources overwrite earlier sources.
  readonly sources: ISource[];

  // Bound source-archive handling and object selection. Exclusions constrain stale deletion.
  readonly sourceProcessing?: {
    readonly extract?: boolean; // Default: true
    readonly include?: string[]; // Default: include every non-excluded path
    readonly exclude?: string[]; // Default: exclude nothing
  };

  // Current S3 destination location.
  readonly destination: {
    readonly bucket: Bucket;
    readonly keyPrefix?: string; // Default: bucket root
  };

  // Explicit destructive behavior by CloudFormation lifecycle phase.
  readonly destinationLifecycle?: {
    readonly onDeploy?: {
      readonly deleteStaleObjects?: boolean; // Default: true
    };
    readonly onChange?: {
      readonly deletePreviousObjects?: boolean; // Default: false
      readonly previousBucket?: IBucket; // Required to delete from a changed bucket
      readonly invalidatePreviousDistribution?: IDistributionRef; // Required to invalidate a changed distribution
    };
    readonly onDelete?: {
      readonly deleteCurrentObjects?: boolean; // Default: false
    };
  };

  // Optional cache invalidation after successful destination work.
  readonly cloudfrontInvalidation?: {
    readonly distribution: IDistributionRef;
    readonly paths?: string[]; // Default: the destination prefix followed by "*"
    readonly waitForCompletion?: boolean; // Default: true
  };

  // Lambda resource, identity, sharing, networking, and build configuration.
  readonly providerLambda?: {
    readonly sharing?: ProviderSharing; // Default: ProviderSharing.STACK
    readonly architecture?: Architecture; // Default: Architecture.ARM_64
    readonly memorySize?: number; // Default: 1024 MiB
    readonly failureDiagnostics?: FailureDiagnostics; // Default: STANDARD
    readonly role?: IRole; // Default: create a shared provider role
    readonly logGroup?: ILogGroupRef; // Default: Lambda-managed log group
    readonly vpc?: IVpc; // Default: no VPC
    readonly vpcSubnets?: SubnetSelection; // Default: VPC default selection
    readonly securityGroups?: ISecurityGroup[]; // Default: dedicated group in a VPC
    readonly localBuild?: {
      readonly projectPath?: string;
      readonly bundling?: ShinBucketDeploymentBundlingOptions;
    }; // Default: use the packaged prebuilt provider
  };

  // Request-scoped controls; these do not split a compatible shared handler.
  readonly transfer?: {
    readonly maxConcurrency?: number; // Default: 32
    readonly advancedTuning?: {
      readonly sourceBlockBytes?: number; // Default: 8 MiB
      readonly sourceBlockMergeGapBytes?: number; // Default: 256 KiB
      readonly sourceGetConcurrency?: number; // Default: derived from Lambda memory
      readonly sourceWindowBytes?: number; // Default: derived from memory and source shape
      readonly sourceWindowMemoryBudgetMiB?: number; // Default: 50% of Lambda memory
      readonly destinationWriteRetry?: {
        readonly maxAttempts?: number; // Default: 6
        readonly baseDelayMs?: number; // Default: 250
        readonly maxDelayMs?: number; // Default: 5000
        readonly slowdownBaseDelayMs?: number; // Default: 1000
        readonly slowdownMaxDelayMs?: number; // Default: 30000
        readonly jitter?: DestinationWriteRetryJitter; // Default: FULL
      };
    };
  };
}

Most consumers should omit providerLambda.localBuild and use the packaged provider. See Building from source when you need to compile it yourself.

Outputs and Methods

The constructed deployment exposes these values and methods; these are construct members, not CloudFormation outputs or input props.

| Member | Description | | --- | --- | | deployment.deployedBucket | Destination bucket reconstructed lazily from the custom-resource response. | | deployment.objectKeys | Deployed object keys requested lazily from the custom-resource response. | | deployment.handlerRole | Execution role used by the backing provider Lambda. | | deployment.handlerFunction | Backing provider Lambda function. | | deployment.addSource(source) | Adds another ordered source after construction. |

BucketDeployment Property Mapping

ShinBucketDeployment accepts the same upstream CDK ISource implementations, but uses its own grouped configuration API. Keep sources, move the destination into destination, and then map only the behavior your deployment needs.

Replaced Properties

| Upstream prop | Use instead | | --- | --- | | prune | destinationLifecycle.onDeploy.deleteStaleObjects | | retainOnDelete | destinationLifecycle.onChange.deletePreviousObjects and destinationLifecycle.onDelete.deleteCurrentObjects | | distribution | cloudfrontInvalidation.distribution | | distributionPaths | cloudfrontInvalidation.paths | | waitForDistributionInvalidation | cloudfrontInvalidation.waitForCompletion | | outputObjectKeys | objectKeys; Shin returns the key list only when this property is accessed. | | logRetention | providerLambda.logGroup | | serverSideEncryption, serverSideEncryptionAwsKmsKeyId | Default encryption on destination.bucket |

Unsupported Properties

| Upstream prop | Shin behavior | | --- | --- | | accessControl, cacheControl, contentDisposition, contentEncoding, contentLanguage, expires, metadata, storageClass, websiteRedirectLocation | Object metadata is intentionally outside the deployment contract. Configure cache behavior in CloudFront and storage/lifecycle behavior on the bucket. | | contentType | Shin automatically infers Content-Type from each deployed object's file extension, with application/octet-stream as the fallback. | | serverSideEncryptionCustomerAlgorithm | SSE-C is not supported. | | ephemeralStorageSize | The provider does not stage archives or extracted files in Lambda /tmp. | | signContent | The provider uses AWS SDK calls directly, not the upstream AWS CLI upload path. | | useEfs | EFS is not needed because the provider streams data with bounded memory instead of staging archives or extracted files on disk. |

Destination Lifecycle

Most deployments should omit destinationLifecycle. By default, Shin removes stale objects from the current destination on Create and Update, retains previous objects after a destination change, and retains current objects when the stack or custom resource is deleted. It never deletes the bucket or CloudFront distribution resources themselves.

Deployment

[!WARNING] destinationLifecycle.onDeploy.deleteStaleObjects defaults to true and assumes this deployment owns the destination prefix. Set it to false if other tools or users write objects there that must be preserved.

Destination Change

onChange applies only when an update changes destination.keyPrefix, destination.bucket, or cloudfrontInvalidation.distribution and you want to act on the previous location. Use the relevant table for each action and combine the settings when both are needed.

Object Cleanup

Previous objects are retained by default. To delete them:

| destination.bucket | destination.keyPrefix | Object-cleanup configuration | | --- | --- | --- | | Unchanged | Unchanged | None; there is no previous object location. | | Unchanged | Changed | Set deletePreviousObjects: true. Omit previousBucket; Shin uses the current bucket. | | Changed | Unchanged | Set deletePreviousObjects: true and provide previousBucket. | | Changed | Changed | Set deletePreviousObjects: true and provide previousBucket. |

CloudFront Invalidation

| cloudfrontInvalidation.distribution | Invalidation configuration | | --- | --- | | Unchanged | Omit invalidatePreviousDistribution; any configured current distribution is invalidated normally. | | Changed | Provide invalidatePreviousDistribution: previousDistribution only if the previous distribution should also be invalidated. |

[!IMPORTANT] After a one-time destination move succeeds, remove previous-resource references and any onChange actions that are no longer needed to drop access to the previous bucket or distribution.

Resource Deletion

Set destinationLifecycle.onDelete.deleteCurrentObjects to true only when current destination objects should be removed with the stack or custom resource. Otherwise, omit it.

How It Works

Shin plans extracted assets directly from ranged S3 reads without staging complete archives or entries in Lambda /tmp. It compares the deployment plan with the destination, streams changed objects with bounded concurrency and memory, optionally removes stale objects, and then creates any configured CloudFront invalidation. Direct-copy sources use S3 CopyObject instead of extraction.

Content checks and write reconciliation adapt to the destination bucket's encryption. Transfer failures stop later cleanup and invalidation, and structured diagnostics are emitted without resource identifiers or presigned URLs.

See Architecture for the handler flow, archive planning, memory model, retry and write-safety rules, marker replacement, lifecycle behavior, compatibility tradeoffs, limits, and diagnostics field reference. The latest correctness snapshot is recorded in Verification.