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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@richicoder/cdk-tcr

v0.3.2

Published

CDK Typesafe Resource Provider

Downloads

10

Readme

cdk-tcr

CDK TypeSafe Custom Resource Provider.

Provider the following:

  • Validation of incoming properties using the excellent zod library.
  • Automatic camel casing of incoming properties for a more Javascript/Typescript feel.
  • Automatic coercion of incoming properties from CloudFormation's stringified values.
  • Constructs for creating provider resources on a stack-singleton and account-wide basis.
  • Base constructs for creating CustomResource types with type validation of inputs.

Note This library is intended to only work with Javascript/Typescript providers and constructs. Support for JSII and other languages is a non-goal due to JSII's lack of flexibility and overhead around type information. You can use the provider helper to write the Provider and still get type safety, will need to write your Custom Resource manually as described in the docs in order to use JSII.

Creating a Provider Handler

To define a simple resource, you can implement the handler by first defining a ResourceDefinition, which is the Resource's schema and type name.

stack/resource.ts

import { cf, ResourceDefinition } from '@richicoder1/cdk-tcr';
import { z } from 'zod';

// Define if using account-wide resource providers (see below)
// export const providerId = "exampleProvider";

export const exampleResourceDef = {
  schema: z.object({
    // Note the use of cf. 
    // These helpers handle automatically coercing from CloudFormation's stringified values and downcased using camelcase-keys.
    // Only necessary for primitives, all other zod types should work (though non-string enums will require a preprocess step).
    path: cf.string,
    optionalBool: cf.boolean.optional(),
    literal: cf.literal(false, cf.boolean),
  }),
  typeName: "Custom::Example::CustomResource",
} satisfies ResourceDefinition<any>;

export type ExampleResourceType = typeof exampleResourceDef;

Then, in your handler, you define the lifecycle of the resource using the provider builder.

stack/handler/index.ts

import { provider } from "@richicoder/cdk-tcr/handler";
import { resource } from "../resource";

export const onEvent = provider()
    .resource(resource, {
        async create(properties /*: { path: string, optionalBool: bool | null, literal: false } */) {
            // create the thing
            return {
                physicalResourceId: 'some-id',
            };
        },
        async update(id /*: string */, properties, oldProperties) {
            return {};
        },
        async delete(id, properties) {
            return {};
        },
    })
    .build();

Creating a Provider

You can either manually create the provider following the instructions in the documentation or use the helper TypedProvider class:

stack/provider.ts

import { Construct } from 'constructs';
import { Code } from 'aws-cdk-lib/aws-lambda';
import { TypedProvider } from "@richicoder1/cdk-tcr/provider";
import path from 'node:path';
import { providerId } from './resource';

export class ExampleResourceProvider extends TypedProvider {
  constructor(scope: Construct, id: string) {
    super(scope, id, {
      // Function defaults to node16 function
      // You can also pass in FunctionProps or an IFunction (either from the CDK or SST)
      onEvent: Code.fromAsset(path.join(__dirname, 'handler'))
    });
  }
}

Then you can use TypedCustomResource to create your new resource. This class not only wraps CustomResource, but automatically validates inputs so that users get immediate validation.

stack/ExampleResource.ts

import { Construct } from 'constructs';
import { TypedCustomResource, TypedCustomResourcePropsBase } from "@richicoder1/cdk-tcr/customResource";
import { exampleResourceDef, ExampleResourceType } from "./resource";
import { ExampleResourceProvider } from "./provider";

export type ExampleResourceProps = TypedCustomResourcePropsBase<ExampleResourceType>;

export class ExampleResource extends TypedCustomResource<ExampleResourceProps, ExampleResourceType> {
  constructor(scope: Construct, id: string, props: ExampleResourceProps) {
    super(scope, id, props, {
      resource: exampleResourceDef,
      // Will automatically create the provider if it doesn't exist, or use the existing one.
      providerClass: ExampleResourceProvider,
    });
  }
}

Using in a Stack

Now that you have your custom resource, you're ready to use it in a stack like so:

stack/index.ts

Note This is using the Serverless Stack style functional stack, but also works with a normal stack.

import { StackProps } from "sst/constructs";
import { ExampleResource } from "./ExampleResource";

export function MyStack({ stack }: StackProps) {
    new ExampleResource(this, "Example", {
        // This will have type completion and validation.
        path: './example',
        literal: false,
    });
}

Account-Wide Resource

If you'd like to register a provider in an account once and reuse it in multiple projects, you can use registerAccountWide and provider to share the provider.

stack/provider.ts

import { Construct } from 'constructs';
import { Code } from 'aws-cdk-lib/aws-lambda';
import { TypedProvider } from "@richicoder1/cdk-tcr/provider";
import path from 'node:path';
import { providerId } from './resource';

export class ExampleResourceProvider extends TypedProvider {
  constructor(scope: Construct, id: string) {
    super(scope, id, {
      onEvent: Code.fromAsset(path.join(__dirname, 'handler')),
      // Automatically registers the account token under
      //   /cdk-tcr/providers/${providerId}
      // The prefix is configurable
      registerAccountWide: true
      providerId,
    });
  }
}

stack/ExampleResource.ts

Note This could be, for example, shared inside an NPM package

import { Construct } from 'constructs';
import { getRegisteredServiceToken } from '@richicoder1/cdk-tcr';
import { TypedCustomResource, TypedCustomResourcePropsBase } from "@richicoder1/cdk-tcr/customResource";
import { exampleResourceDef, ExampleResourceType, providerId } from "../shared";

export type ExampleResourceProps = TypedCustomResourcePropsBase<ExampleResourceType>;

export class ExampleResource extends TypedCustomResource<ExampleResourceProps, ExampleResourceType> {
  constructor(scope: Construct, id: string, props: ExampleResourceProps) {
    super(scope, id, props, {
      resource: exampleResourceDef,
      // Automatically looks up the SSM parameter under
      //   /cdk-tcr/providers/${providerId}
      // The prefix is configurable
      provider: providerId,
    });
  }
}