├── .cursor
└── rules
│ ├── cloudflare.mdc
│ └── vibe-tools.mdc
├── .cursorrules
├── .github
└── workflows
│ ├── pkg-pr.yml
│ ├── publish.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .gitmodules
├── .vscode
└── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── alchemy-web
├── .gitignore
├── .vitepress
│ ├── config.mts
│ └── theme
│ │ ├── components
│ │ └── CodeSnippetHero.vue
│ │ ├── fonts
│ │ └── Inter.ttf
│ │ ├── index.ts
│ │ ├── og-generator.ts
│ │ └── style.css
├── blogs
│ ├── 2025-04-08-decade-long-journey.md
│ └── index.md
├── docs
│ ├── advanced
│ │ └── serde.md
│ ├── concepts
│ │ ├── adoption.md
│ │ ├── bindings.md
│ │ ├── destroy.md
│ │ ├── phase.md
│ │ ├── resource.md
│ │ ├── scope.md
│ │ ├── secret.md
│ │ ├── state.md
│ │ └── testing.md
│ ├── getting-started.md
│ ├── guides
│ │ ├── cloudflare-auth.md
│ │ ├── cloudflare-durable-objects.md
│ │ ├── cloudflare-nuxt-pipeline.md
│ │ ├── cloudflare-queue.md
│ │ ├── cloudflare-react-router.md
│ │ ├── cloudflare-redwood.md
│ │ ├── cloudflare-tanstack-start.md
│ │ ├── cloudflare-vitejs.md
│ │ ├── cloudflare-worker.md
│ │ ├── cloudflare-workflows.md
│ │ ├── custom-resources.md
│ │ └── custom-state-store.md
│ ├── index.md
│ ├── providers
│ │ ├── ai
│ │ │ ├── astro-file.md
│ │ │ ├── css-file.md
│ │ │ ├── data.md
│ │ │ ├── document.md
│ │ │ ├── html-file.md
│ │ │ ├── json-file.md
│ │ │ ├── typescript-file.md
│ │ │ ├── vue-file.md
│ │ │ └── yaml-file.md
│ │ ├── aws-control
│ │ │ ├── access-analyzer
│ │ │ │ └── analyzer.md
│ │ │ ├── acmpca
│ │ │ │ ├── certificate-authority-activation.md
│ │ │ │ ├── certificate-authority.md
│ │ │ │ ├── certificate.md
│ │ │ │ └── permission.md
│ │ │ ├── alexa
│ │ │ │ └── ask.md
│ │ │ ├── amazon-mq
│ │ │ │ ├── broker.md
│ │ │ │ ├── configuration-association.md
│ │ │ │ └── configuration.md
│ │ │ ├── amplify-uibuilder
│ │ │ │ ├── component.md
│ │ │ │ ├── form.md
│ │ │ │ └── theme.md
│ │ │ ├── amplify
│ │ │ │ ├── app.md
│ │ │ │ ├── branch.md
│ │ │ │ └── domain.md
│ │ │ ├── api-gateway-v2
│ │ │ │ ├── api-gateway-managed-overrides.md
│ │ │ │ ├── api-mapping.md
│ │ │ │ ├── api.md
│ │ │ │ ├── authorizer.md
│ │ │ │ ├── deployment.md
│ │ │ │ ├── domain-name.md
│ │ │ │ ├── integration-response.md
│ │ │ │ ├── integration.md
│ │ │ │ ├── model.md
│ │ │ │ ├── route-response.md
│ │ │ │ ├── route.md
│ │ │ │ ├── stage.md
│ │ │ │ └── vpc-link.md
│ │ │ ├── api-gateway
│ │ │ │ ├── account.md
│ │ │ │ ├── api-key.md
│ │ │ │ ├── authorizer.md
│ │ │ │ ├── base-path-mapping-v2.md
│ │ │ │ ├── base-path-mapping.md
│ │ │ │ ├── client-certificate.md
│ │ │ │ ├── deployment.md
│ │ │ │ ├── documentation-part.md
│ │ │ │ ├── documentation-version.md
│ │ │ │ ├── domain-name-access-association.md
│ │ │ │ ├── domain-name-v2.md
│ │ │ │ ├── domain-name.md
│ │ │ │ ├── gateway-response.md
│ │ │ │ ├── method.md
│ │ │ │ ├── model.md
│ │ │ │ ├── request-validator.md
│ │ │ │ ├── resource.md
│ │ │ │ ├── rest-api.md
│ │ │ │ ├── stage.md
│ │ │ │ ├── usage-plan-key.md
│ │ │ │ ├── usage-plan.md
│ │ │ │ └── vpc-link.md
│ │ │ ├── app-config
│ │ │ │ ├── application.md
│ │ │ │ ├── configuration-profile.md
│ │ │ │ ├── deployment-strategy.md
│ │ │ │ ├── deployment.md
│ │ │ │ ├── environment.md
│ │ │ │ ├── extension-association.md
│ │ │ │ ├── extension.md
│ │ │ │ └── hosted-configuration-version.md
│ │ │ ├── app-flow
│ │ │ │ ├── connector-profile.md
│ │ │ │ ├── connector.md
│ │ │ │ └── flow.md
│ │ │ ├── app-integrations
│ │ │ │ ├── application.md
│ │ │ │ ├── data-integration.md
│ │ │ │ └── event-integration.md
│ │ │ ├── app-mesh
│ │ │ │ ├── gateway-route.md
│ │ │ │ ├── mesh.md
│ │ │ │ ├── route.md
│ │ │ │ ├── virtual-gateway.md
│ │ │ │ ├── virtual-node.md
│ │ │ │ ├── virtual-router.md
│ │ │ │ └── virtual-service.md
│ │ │ ├── app-runner
│ │ │ │ ├── auto-scaling-configuration.md
│ │ │ │ ├── observability-configuration.md
│ │ │ │ ├── service.md
│ │ │ │ ├── vpc-connector.md
│ │ │ │ └── vpc-ingress-connection.md
│ │ │ ├── app-stream
│ │ │ │ ├── app-block-builder.md
│ │ │ │ ├── app-block.md
│ │ │ │ ├── application-entitlement-association.md
│ │ │ │ ├── application-fleet-association.md
│ │ │ │ ├── application.md
│ │ │ │ ├── directory-config.md
│ │ │ │ ├── entitlement.md
│ │ │ │ ├── fleet.md
│ │ │ │ ├── image-builder.md
│ │ │ │ ├── stack-fleet-association.md
│ │ │ │ ├── stack-user-association.md
│ │ │ │ ├── stack.md
│ │ │ │ └── user.md
│ │ │ ├── app-sync
│ │ │ │ ├── api-cache.md
│ │ │ │ ├── api-key.md
│ │ │ │ ├── api.md
│ │ │ │ ├── channel-namespace.md
│ │ │ │ ├── data-source.md
│ │ │ │ ├── domain-name-api-association.md
│ │ │ │ ├── domain-name.md
│ │ │ │ ├── function-configuration.md
│ │ │ │ ├── graph-qlapi.md
│ │ │ │ ├── graph-qlschema.md
│ │ │ │ ├── resolver.md
│ │ │ │ └── source-api-association.md
│ │ │ ├── app-test
│ │ │ │ └── test-case.md
│ │ │ ├── application-auto-scaling
│ │ │ │ ├── scalable-target.md
│ │ │ │ └── scaling-policy.md
│ │ │ ├── application-insights
│ │ │ │ └── application.md
│ │ │ ├── application-signals
│ │ │ │ ├── discovery.md
│ │ │ │ └── service-level-objective.md
│ │ │ ├── aps
│ │ │ │ ├── rule-groups-namespace.md
│ │ │ │ ├── scraper.md
│ │ │ │ └── workspace.md
│ │ │ ├── arczonal-shift
│ │ │ │ ├── autoshift-observer-notification-status.md
│ │ │ │ └── zonal-autoshift-configuration.md
│ │ │ ├── athena
│ │ │ │ ├── capacity-reservation.md
│ │ │ │ ├── data-catalog.md
│ │ │ │ ├── named-query.md
│ │ │ │ ├── prepared-statement.md
│ │ │ │ └── work-group.md
│ │ │ ├── audit-manager
│ │ │ │ └── assessment.md
│ │ │ ├── auto-scaling-plans
│ │ │ │ └── scaling-plan.md
│ │ │ ├── auto-scaling
│ │ │ │ ├── auto-scaling-group.md
│ │ │ │ ├── launch-configuration.md
│ │ │ │ ├── lifecycle-hook.md
│ │ │ │ ├── scaling-policy.md
│ │ │ │ ├── scheduled-action.md
│ │ │ │ └── warm-pool.md
│ │ │ ├── b2bi
│ │ │ │ ├── capability.md
│ │ │ │ ├── partnership.md
│ │ │ │ ├── profile.md
│ │ │ │ └── transformer.md
│ │ │ ├── backup-gateway
│ │ │ │ └── hypervisor.md
│ │ │ ├── backup
│ │ │ │ ├── backup-plan.md
│ │ │ │ ├── backup-selection.md
│ │ │ │ ├── backup-vault.md
│ │ │ │ ├── framework.md
│ │ │ │ ├── logically-air-gapped-backup-vault.md
│ │ │ │ ├── report-plan.md
│ │ │ │ ├── restore-testing-plan.md
│ │ │ │ └── restore-testing-selection.md
│ │ │ ├── batch
│ │ │ │ ├── compute-environment.md
│ │ │ │ ├── consumable-resource.md
│ │ │ │ ├── job-definition.md
│ │ │ │ ├── job-queue.md
│ │ │ │ └── scheduling-policy.md
│ │ │ ├── bcmdata-exports
│ │ │ │ └── export.md
│ │ │ ├── bedrock
│ │ │ │ ├── agent-alias.md
│ │ │ │ ├── agent.md
│ │ │ │ ├── application-inference-profile.md
│ │ │ │ ├── blueprint.md
│ │ │ │ ├── data-automation-project.md
│ │ │ │ ├── data-source.md
│ │ │ │ ├── flow-alias.md
│ │ │ │ ├── flow-version.md
│ │ │ │ ├── flow.md
│ │ │ │ ├── guardrail-version.md
│ │ │ │ ├── guardrail.md
│ │ │ │ ├── knowledge-base.md
│ │ │ │ ├── prompt-version.md
│ │ │ │ └── prompt.md
│ │ │ ├── billing-conductor
│ │ │ │ ├── billing-group.md
│ │ │ │ ├── custom-line-item.md
│ │ │ │ ├── pricing-plan.md
│ │ │ │ └── pricing-rule.md
│ │ │ ├── budgets
│ │ │ │ ├── budget.md
│ │ │ │ └── budgets-action.md
│ │ │ ├── cassandra
│ │ │ │ ├── keyspace.md
│ │ │ │ ├── table.md
│ │ │ │ └── type.md
│ │ │ ├── ce
│ │ │ │ ├── anomaly-monitor.md
│ │ │ │ ├── anomaly-subscription.md
│ │ │ │ └── cost-category.md
│ │ │ ├── certificate-manager
│ │ │ │ ├── account.md
│ │ │ │ └── certificate.md
│ │ │ ├── chatbot
│ │ │ │ ├── custom-action.md
│ │ │ │ ├── microsoft-teams-channel-configuration.md
│ │ │ │ └── slack-channel-configuration.md
│ │ │ ├── clean-rooms-ml
│ │ │ │ └── training-dataset.md
│ │ │ ├── clean-rooms
│ │ │ │ ├── analysis-template.md
│ │ │ │ ├── collaboration.md
│ │ │ │ ├── configured-table-association.md
│ │ │ │ ├── configured-table.md
│ │ │ │ ├── id-mapping-table.md
│ │ │ │ ├── id-namespace-association.md
│ │ │ │ ├── membership.md
│ │ │ │ └── privacy-budget-template.md
│ │ │ ├── cloud-formation
│ │ │ │ ├── custom-resource.md
│ │ │ │ ├── guard-hook.md
│ │ │ │ ├── hook-default-version.md
│ │ │ │ ├── hook-type-config.md
│ │ │ │ ├── hook-version.md
│ │ │ │ ├── lambda-hook.md
│ │ │ │ ├── macro.md
│ │ │ │ ├── module-default-version.md
│ │ │ │ ├── module-version.md
│ │ │ │ ├── public-type-version.md
│ │ │ │ ├── publisher.md
│ │ │ │ ├── resource-default-version.md
│ │ │ │ ├── resource-version.md
│ │ │ │ ├── stack-set.md
│ │ │ │ ├── stack.md
│ │ │ │ ├── type-activation.md
│ │ │ │ ├── wait-condition-handle.md
│ │ │ │ └── wait-condition.md
│ │ │ ├── cloud-front
│ │ │ │ ├── anycast-ip-list.md
│ │ │ │ ├── cache-policy.md
│ │ │ │ ├── cloud-front-origin-access-identity.md
│ │ │ │ ├── connection-group.md
│ │ │ │ ├── continuous-deployment-policy.md
│ │ │ │ ├── distribution-tenant.md
│ │ │ │ ├── distribution.md
│ │ │ │ ├── function.md
│ │ │ │ ├── key-group.md
│ │ │ │ ├── key-value-store.md
│ │ │ │ ├── monitoring-subscription.md
│ │ │ │ ├── origin-access-control.md
│ │ │ │ ├── origin-request-policy.md
│ │ │ │ ├── public-key.md
│ │ │ │ ├── realtime-log-config.md
│ │ │ │ ├── response-headers-policy.md
│ │ │ │ ├── streaming-distribution.md
│ │ │ │ └── vpc-origin.md
│ │ │ ├── cloud-trail
│ │ │ │ ├── channel.md
│ │ │ │ ├── dashboard.md
│ │ │ │ ├── event-data-store.md
│ │ │ │ ├── resource-policy.md
│ │ │ │ └── trail.md
│ │ │ ├── cloud-watch
│ │ │ │ ├── alarm.md
│ │ │ │ ├── anomaly-detector.md
│ │ │ │ ├── composite-alarm.md
│ │ │ │ ├── dashboard.md
│ │ │ │ ├── insight-rule.md
│ │ │ │ └── metric-stream.md
│ │ │ ├── cloud9
│ │ │ │ └── environment-ec2.md
│ │ │ ├── code-artifact
│ │ │ │ ├── domain.md
│ │ │ │ ├── package-group.md
│ │ │ │ └── repository.md
│ │ │ ├── code-build
│ │ │ │ ├── fleet.md
│ │ │ │ ├── project.md
│ │ │ │ ├── report-group.md
│ │ │ │ └── source-credential.md
│ │ │ ├── code-commit
│ │ │ │ └── repository.md
│ │ │ ├── code-connections
│ │ │ │ └── connection.md
│ │ │ ├── code-deploy
│ │ │ │ ├── application.md
│ │ │ │ ├── deployment-config.md
│ │ │ │ └── deployment-group.md
│ │ │ ├── code-guru-profiler
│ │ │ │ └── profiling-group.md
│ │ │ ├── code-guru-reviewer
│ │ │ │ └── repository-association.md
│ │ │ ├── code-pipeline
│ │ │ │ ├── custom-action-type.md
│ │ │ │ ├── pipeline.md
│ │ │ │ └── webhook.md
│ │ │ ├── code-star-connections
│ │ │ │ ├── connection.md
│ │ │ │ ├── repository-link.md
│ │ │ │ └── sync-configuration.md
│ │ │ ├── code-star-notifications
│ │ │ │ └── notification-rule.md
│ │ │ ├── code-star
│ │ │ │ └── git-hub-repository.md
│ │ │ ├── cognito
│ │ │ │ ├── identity-pool-principal-tag.md
│ │ │ │ ├── identity-pool-role-attachment.md
│ │ │ │ ├── identity-pool.md
│ │ │ │ ├── log-delivery-configuration.md
│ │ │ │ ├── managed-login-branding.md
│ │ │ │ ├── user-pool-client.md
│ │ │ │ ├── user-pool-domain.md
│ │ │ │ ├── user-pool-group.md
│ │ │ │ ├── user-pool-identity-provider.md
│ │ │ │ ├── user-pool-resource-server.md
│ │ │ │ ├── user-pool-risk-configuration-attachment.md
│ │ │ │ ├── user-pool-uicustomization-attachment.md
│ │ │ │ ├── user-pool-user-to-group-attachment.md
│ │ │ │ ├── user-pool-user.md
│ │ │ │ └── user-pool.md
│ │ │ ├── comprehend
│ │ │ │ ├── document-classifier.md
│ │ │ │ └── flywheel.md
│ │ │ ├── config
│ │ │ │ ├── aggregation-authorization.md
│ │ │ │ ├── config-rule.md
│ │ │ │ ├── configuration-aggregator.md
│ │ │ │ ├── configuration-recorder.md
│ │ │ │ ├── conformance-pack.md
│ │ │ │ ├── delivery-channel.md
│ │ │ │ ├── organization-config-rule.md
│ │ │ │ ├── organization-conformance-pack.md
│ │ │ │ ├── remediation-configuration.md
│ │ │ │ └── stored-query.md
│ │ │ ├── connect-campaigns-v2
│ │ │ │ └── campaign.md
│ │ │ ├── connect-campaigns
│ │ │ │ └── campaign.md
│ │ │ ├── connect
│ │ │ │ ├── agent-status.md
│ │ │ │ ├── approved-origin.md
│ │ │ │ ├── contact-flow-module.md
│ │ │ │ ├── contact-flow-version.md
│ │ │ │ ├── contact-flow.md
│ │ │ │ ├── email-address.md
│ │ │ │ ├── evaluation-form.md
│ │ │ │ ├── hours-of-operation.md
│ │ │ │ ├── instance-storage-config.md
│ │ │ │ ├── instance.md
│ │ │ │ ├── integration-association.md
│ │ │ │ ├── phone-number.md
│ │ │ │ ├── predefined-attribute.md
│ │ │ │ ├── prompt.md
│ │ │ │ ├── queue.md
│ │ │ │ ├── quick-connect.md
│ │ │ │ ├── routing-profile.md
│ │ │ │ ├── rule.md
│ │ │ │ ├── security-key.md
│ │ │ │ ├── security-profile.md
│ │ │ │ ├── task-template.md
│ │ │ │ ├── traffic-distribution-group.md
│ │ │ │ ├── user-hierarchy-group.md
│ │ │ │ ├── user-hierarchy-structure.md
│ │ │ │ ├── user.md
│ │ │ │ ├── view-version.md
│ │ │ │ └── view.md
│ │ │ ├── control-tower
│ │ │ │ ├── enabled-baseline.md
│ │ │ │ ├── enabled-control.md
│ │ │ │ └── landing-zone.md
│ │ │ ├── cur
│ │ │ │ └── report-definition.md
│ │ │ ├── customer-profiles
│ │ │ │ ├── calculated-attribute-definition.md
│ │ │ │ ├── domain.md
│ │ │ │ ├── event-stream.md
│ │ │ │ ├── event-trigger.md
│ │ │ │ ├── integration.md
│ │ │ │ ├── object-type.md
│ │ │ │ └── segment-definition.md
│ │ │ ├── data-brew
│ │ │ │ ├── dataset.md
│ │ │ │ ├── job.md
│ │ │ │ ├── project.md
│ │ │ │ ├── recipe.md
│ │ │ │ ├── ruleset.md
│ │ │ │ └── schedule.md
│ │ │ ├── data-pipeline
│ │ │ │ └── pipeline.md
│ │ │ ├── data-sync
│ │ │ │ ├── agent.md
│ │ │ │ ├── location-azure-blob.md
│ │ │ │ ├── location-efs.md
│ │ │ │ ├── location-fsx-lustre.md
│ │ │ │ ├── location-fsx-ontap.md
│ │ │ │ ├── location-fsx-open-zfs.md
│ │ │ │ ├── location-fsx-windows.md
│ │ │ │ ├── location-hdfs.md
│ │ │ │ ├── location-nfs.md
│ │ │ │ ├── location-object-storage.md
│ │ │ │ ├── location-s3.md
│ │ │ │ ├── location-smb.md
│ │ │ │ ├── storage-system.md
│ │ │ │ └── task.md
│ │ │ ├── data-zone
│ │ │ │ ├── connection.md
│ │ │ │ ├── data-source.md
│ │ │ │ ├── domain.md
│ │ │ │ ├── environment-actions.md
│ │ │ │ ├── environment-blueprint-configuration.md
│ │ │ │ ├── environment-profile.md
│ │ │ │ ├── environment.md
│ │ │ │ ├── group-profile.md
│ │ │ │ ├── project-membership.md
│ │ │ │ ├── project.md
│ │ │ │ ├── subscription-target.md
│ │ │ │ └── user-profile.md
│ │ │ ├── dax
│ │ │ │ ├── cluster.md
│ │ │ │ ├── parameter-group.md
│ │ │ │ └── subnet-group.md
│ │ │ ├── deadline
│ │ │ │ ├── farm.md
│ │ │ │ ├── fleet.md
│ │ │ │ ├── license-endpoint.md
│ │ │ │ ├── limit.md
│ │ │ │ ├── metered-product.md
│ │ │ │ ├── monitor.md
│ │ │ │ ├── queue-environment.md
│ │ │ │ ├── queue-fleet-association.md
│ │ │ │ ├── queue-limit-association.md
│ │ │ │ ├── queue.md
│ │ │ │ └── storage-profile.md
│ │ │ ├── detective
│ │ │ │ ├── graph.md
│ │ │ │ ├── member-invitation.md
│ │ │ │ └── organization-admin.md
│ │ │ ├── dev-ops-guru
│ │ │ │ ├── log-anomaly-detection-integration.md
│ │ │ │ ├── notification-channel.md
│ │ │ │ └── resource-collection.md
│ │ │ ├── directory-service
│ │ │ │ ├── microsoft-ad.md
│ │ │ │ └── simple-ad.md
│ │ │ ├── dlm
│ │ │ │ └── lifecycle-policy.md
│ │ │ ├── dms
│ │ │ │ ├── certificate.md
│ │ │ │ ├── data-migration.md
│ │ │ │ ├── data-provider.md
│ │ │ │ ├── endpoint.md
│ │ │ │ ├── event-subscription.md
│ │ │ │ ├── instance-profile.md
│ │ │ │ ├── migration-project.md
│ │ │ │ ├── replication-config.md
│ │ │ │ ├── replication-instance.md
│ │ │ │ ├── replication-subnet-group.md
│ │ │ │ └── replication-task.md
│ │ │ ├── doc-db
│ │ │ │ ├── dbcluster-parameter-group.md
│ │ │ │ ├── dbcluster.md
│ │ │ │ ├── dbinstance.md
│ │ │ │ ├── dbsubnet-group.md
│ │ │ │ └── event-subscription.md
│ │ │ ├── doc-dbelastic
│ │ │ │ └── cluster.md
│ │ │ ├── dsql
│ │ │ │ └── cluster.md
│ │ │ ├── dynamo-db
│ │ │ │ ├── global-table.md
│ │ │ │ └── table.md
│ │ │ ├── ec2
│ │ │ │ ├── capacity-reservation-fleet.md
│ │ │ │ ├── capacity-reservation.md
│ │ │ │ ├── carrier-gateway.md
│ │ │ │ ├── client-vpn-authorization-rule.md
│ │ │ │ ├── client-vpn-endpoint.md
│ │ │ │ ├── client-vpn-route.md
│ │ │ │ ├── client-vpn-target-network-association.md
│ │ │ │ ├── customer-gateway.md
│ │ │ │ ├── dhcpoptions.md
│ │ │ │ ├── ec2fleet.md
│ │ │ │ ├── egress-only-internet-gateway.md
│ │ │ │ ├── eip.md
│ │ │ │ ├── eipassociation.md
│ │ │ │ ├── enclave-certificate-iam-role-association.md
│ │ │ │ ├── flow-log.md
│ │ │ │ ├── gateway-route-table-association.md
│ │ │ │ ├── host.md
│ │ │ │ ├── instance-connect-endpoint.md
│ │ │ │ ├── instance.md
│ │ │ │ ├── internet-gateway.md
│ │ │ │ ├── ipam.md
│ │ │ │ ├── ipamallocation.md
│ │ │ │ ├── ipampool-cidr.md
│ │ │ │ ├── ipampool.md
│ │ │ │ ├── ipamresource-discovery-association.md
│ │ │ │ ├── ipamresource-discovery.md
│ │ │ │ ├── ipamscope.md
│ │ │ │ ├── key-pair.md
│ │ │ │ ├── launch-template.md
│ │ │ │ ├── local-gateway-route-table-virtual-interface-group-association.md
│ │ │ │ ├── local-gateway-route-table-vpcassociation.md
│ │ │ │ ├── local-gateway-route-table.md
│ │ │ │ ├── local-gateway-route.md
│ │ │ │ ├── nat-gateway.md
│ │ │ │ ├── network-acl-entry.md
│ │ │ │ ├── network-acl.md
│ │ │ │ ├── network-insights-access-scope-analysis.md
│ │ │ │ ├── network-insights-access-scope.md
│ │ │ │ ├── network-insights-analysis.md
│ │ │ │ ├── network-insights-path.md
│ │ │ │ ├── network-interface-attachment.md
│ │ │ │ ├── network-interface-permission.md
│ │ │ │ ├── network-interface.md
│ │ │ │ ├── network-performance-metric-subscription.md
│ │ │ │ ├── placement-group.md
│ │ │ │ ├── prefix-list.md
│ │ │ │ ├── route-server-association.md
│ │ │ │ ├── route-server-endpoint.md
│ │ │ │ ├── route-server-peer.md
│ │ │ │ ├── route-server-propagation.md
│ │ │ │ ├── route-server.md
│ │ │ │ ├── route-table.md
│ │ │ │ ├── route.md
│ │ │ │ ├── security-group-egress.md
│ │ │ │ ├── security-group-ingress.md
│ │ │ │ ├── security-group-vpc-association.md
│ │ │ │ ├── security-group.md
│ │ │ │ ├── snapshot-block-public-access.md
│ │ │ │ ├── spot-fleet.md
│ │ │ │ ├── subnet-cidr-block.md
│ │ │ │ ├── subnet-network-acl-association.md
│ │ │ │ ├── subnet-route-table-association.md
│ │ │ │ ├── subnet.md
│ │ │ │ ├── traffic-mirror-filter-rule.md
│ │ │ │ ├── traffic-mirror-filter.md
│ │ │ │ ├── traffic-mirror-session.md
│ │ │ │ ├── traffic-mirror-target.md
│ │ │ │ ├── transit-gateway-attachment.md
│ │ │ │ ├── transit-gateway-connect.md
│ │ │ │ ├── transit-gateway-multicast-domain-association.md
│ │ │ │ ├── transit-gateway-multicast-domain.md
│ │ │ │ ├── transit-gateway-multicast-group-member.md
│ │ │ │ ├── transit-gateway-multicast-group-source.md
│ │ │ │ ├── transit-gateway-peering-attachment.md
│ │ │ │ ├── transit-gateway-route-table-association.md
│ │ │ │ ├── transit-gateway-route-table-propagation.md
│ │ │ │ ├── transit-gateway-route-table.md
│ │ │ │ ├── transit-gateway-route.md
│ │ │ │ ├── transit-gateway-vpc-attachment.md
│ │ │ │ ├── transit-gateway.md
│ │ │ │ ├── verified-access-endpoint.md
│ │ │ │ ├── verified-access-group.md
│ │ │ │ ├── verified-access-instance.md
│ │ │ │ ├── verified-access-trust-provider.md
│ │ │ │ ├── volume-attachment.md
│ │ │ │ ├── volume.md
│ │ │ │ ├── vpc.md
│ │ │ │ ├── vpcblock-public-access-exclusion.md
│ │ │ │ ├── vpcblock-public-access-options.md
│ │ │ │ ├── vpccidr-block.md
│ │ │ │ ├── vpcdhcpoptions-association.md
│ │ │ │ ├── vpcendpoint-connection-notification.md
│ │ │ │ ├── vpcendpoint-service-permissions.md
│ │ │ │ ├── vpcendpoint-service.md
│ │ │ │ ├── vpcendpoint.md
│ │ │ │ ├── vpcgateway-attachment.md
│ │ │ │ ├── vpcpeering-connection.md
│ │ │ │ ├── vpnconnection-route.md
│ │ │ │ ├── vpnconnection.md
│ │ │ │ ├── vpngateway-route-propagation.md
│ │ │ │ └── vpngateway.md
│ │ │ ├── ecr
│ │ │ │ ├── public-repository.md
│ │ │ │ ├── pull-through-cache-rule.md
│ │ │ │ ├── registry-policy.md
│ │ │ │ ├── registry-scanning-configuration.md
│ │ │ │ ├── replication-configuration.md
│ │ │ │ ├── repository-creation-template.md
│ │ │ │ └── repository.md
│ │ │ ├── ecs
│ │ │ │ ├── capacity-provider.md
│ │ │ │ ├── cluster-capacity-provider-associations.md
│ │ │ │ ├── cluster.md
│ │ │ │ ├── primary-task-set.md
│ │ │ │ ├── service.md
│ │ │ │ ├── task-definition.md
│ │ │ │ └── task-set.md
│ │ │ ├── efs
│ │ │ │ ├── access-point.md
│ │ │ │ ├── file-system.md
│ │ │ │ └── mount-target.md
│ │ │ ├── eks
│ │ │ │ ├── access-entry.md
│ │ │ │ ├── addon.md
│ │ │ │ ├── cluster.md
│ │ │ │ ├── fargate-profile.md
│ │ │ │ ├── identity-provider-config.md
│ │ │ │ ├── nodegroup.md
│ │ │ │ └── pod-identity-association.md
│ │ │ ├── elasti-cache
│ │ │ │ ├── cache-cluster.md
│ │ │ │ ├── global-replication-group.md
│ │ │ │ ├── parameter-group.md
│ │ │ │ ├── replication-group.md
│ │ │ │ ├── security-group-ingress.md
│ │ │ │ ├── security-group.md
│ │ │ │ ├── serverless-cache.md
│ │ │ │ ├── subnet-group.md
│ │ │ │ ├── user-group.md
│ │ │ │ └── user.md
│ │ │ ├── elastic-beanstalk
│ │ │ │ ├── application-version.md
│ │ │ │ ├── application.md
│ │ │ │ ├── configuration-template.md
│ │ │ │ └── environment.md
│ │ │ ├── elastic-load-balancing-v2
│ │ │ │ ├── listener-certificate.md
│ │ │ │ ├── listener-rule.md
│ │ │ │ ├── listener.md
│ │ │ │ ├── load-balancer.md
│ │ │ │ ├── target-group.md
│ │ │ │ ├── trust-store-revocation.md
│ │ │ │ └── trust-store.md
│ │ │ ├── elastic-load-balancing
│ │ │ │ └── load-balancer.md
│ │ │ ├── elasticsearch
│ │ │ │ └── domain.md
│ │ │ ├── emr
│ │ │ │ ├── cluster.md
│ │ │ │ ├── instance-fleet-config.md
│ │ │ │ ├── instance-group-config.md
│ │ │ │ ├── security-configuration.md
│ │ │ │ ├── step.md
│ │ │ │ ├── studio-session-mapping.md
│ │ │ │ ├── studio.md
│ │ │ │ └── walworkspace.md
│ │ │ ├── emrcontainers
│ │ │ │ └── virtual-cluster.md
│ │ │ ├── emrserverless
│ │ │ │ └── application.md
│ │ │ ├── entity-resolution
│ │ │ │ ├── id-mapping-workflow.md
│ │ │ │ ├── id-namespace.md
│ │ │ │ ├── matching-workflow.md
│ │ │ │ ├── policy-statement.md
│ │ │ │ └── schema-mapping.md
│ │ │ ├── event-schemas
│ │ │ │ ├── discoverer.md
│ │ │ │ ├── registry-policy.md
│ │ │ │ ├── registry.md
│ │ │ │ └── schema.md
│ │ │ ├── events
│ │ │ │ ├── api-destination.md
│ │ │ │ ├── archive.md
│ │ │ │ ├── connection.md
│ │ │ │ ├── endpoint.md
│ │ │ │ ├── event-bus-policy.md
│ │ │ │ ├── event-bus.md
│ │ │ │ └── rule.md
│ │ │ ├── evidently
│ │ │ │ ├── experiment.md
│ │ │ │ ├── feature.md
│ │ │ │ ├── launch.md
│ │ │ │ ├── project.md
│ │ │ │ └── segment.md
│ │ │ ├── fin-space
│ │ │ │ └── environment.md
│ │ │ ├── fis
│ │ │ │ ├── experiment-template.md
│ │ │ │ └── target-account-configuration.md
│ │ │ ├── fms
│ │ │ │ ├── notification-channel.md
│ │ │ │ ├── policy.md
│ │ │ │ └── resource-set.md
│ │ │ ├── forecast
│ │ │ │ ├── dataset-group.md
│ │ │ │ └── dataset.md
│ │ │ ├── fraud-detector
│ │ │ │ ├── detector.md
│ │ │ │ ├── entity-type.md
│ │ │ │ ├── event-type.md
│ │ │ │ ├── label.md
│ │ │ │ ├── list.md
│ │ │ │ ├── outcome.md
│ │ │ │ └── variable.md
│ │ │ ├── fsx
│ │ │ │ ├── data-repository-association.md
│ │ │ │ ├── file-system.md
│ │ │ │ ├── snapshot.md
│ │ │ │ ├── storage-virtual-machine.md
│ │ │ │ └── volume.md
│ │ │ ├── game-lift
│ │ │ │ ├── alias.md
│ │ │ │ ├── build.md
│ │ │ │ ├── container-fleet.md
│ │ │ │ ├── container-group-definition.md
│ │ │ │ ├── fleet.md
│ │ │ │ ├── game-server-group.md
│ │ │ │ ├── game-session-queue.md
│ │ │ │ ├── location.md
│ │ │ │ ├── matchmaking-configuration.md
│ │ │ │ ├── matchmaking-rule-set.md
│ │ │ │ └── script.md
│ │ │ ├── global-accelerator
│ │ │ │ ├── accelerator.md
│ │ │ │ ├── cross-account-attachment.md
│ │ │ │ ├── endpoint-group.md
│ │ │ │ └── listener.md
│ │ │ ├── glue
│ │ │ │ ├── classifier.md
│ │ │ │ ├── connection.md
│ │ │ │ ├── crawler.md
│ │ │ │ ├── custom-entity-type.md
│ │ │ │ ├── data-catalog-encryption-settings.md
│ │ │ │ ├── data-quality-ruleset.md
│ │ │ │ ├── database.md
│ │ │ │ ├── dev-endpoint.md
│ │ │ │ ├── job.md
│ │ │ │ ├── mltransform.md
│ │ │ │ ├── partition.md
│ │ │ │ ├── registry.md
│ │ │ │ ├── schema-version-metadata.md
│ │ │ │ ├── schema-version.md
│ │ │ │ ├── schema.md
│ │ │ │ ├── security-configuration.md
│ │ │ │ ├── table-optimizer.md
│ │ │ │ ├── table.md
│ │ │ │ ├── trigger.md
│ │ │ │ ├── usage-profile.md
│ │ │ │ └── workflow.md
│ │ │ ├── grafana
│ │ │ │ └── workspace.md
│ │ │ ├── greengrass-v2
│ │ │ │ ├── component-version.md
│ │ │ │ └── deployment.md
│ │ │ ├── greengrass
│ │ │ │ ├── connector-definition-version.md
│ │ │ │ ├── connector-definition.md
│ │ │ │ ├── core-definition-version.md
│ │ │ │ ├── core-definition.md
│ │ │ │ ├── device-definition-version.md
│ │ │ │ ├── device-definition.md
│ │ │ │ ├── function-definition-version.md
│ │ │ │ ├── function-definition.md
│ │ │ │ ├── group-version.md
│ │ │ │ ├── group.md
│ │ │ │ ├── logger-definition-version.md
│ │ │ │ ├── logger-definition.md
│ │ │ │ ├── resource-definition-version.md
│ │ │ │ ├── resource-definition.md
│ │ │ │ ├── subscription-definition-version.md
│ │ │ │ └── subscription-definition.md
│ │ │ ├── ground-station
│ │ │ │ ├── config.md
│ │ │ │ ├── dataflow-endpoint-group.md
│ │ │ │ └── mission-profile.md
│ │ │ ├── guard-duty
│ │ │ │ ├── detector.md
│ │ │ │ ├── filter.md
│ │ │ │ ├── ipset.md
│ │ │ │ ├── malware-protection-plan.md
│ │ │ │ ├── master.md
│ │ │ │ ├── member.md
│ │ │ │ ├── publishing-destination.md
│ │ │ │ └── threat-intel-set.md
│ │ │ ├── health-imaging
│ │ │ │ └── datastore.md
│ │ │ ├── health-lake
│ │ │ │ └── fhirdatastore.md
│ │ │ ├── iam
│ │ │ │ ├── access-key.md
│ │ │ │ ├── group-policy.md
│ │ │ │ ├── group.md
│ │ │ │ ├── instance-profile.md
│ │ │ │ ├── managed-policy.md
│ │ │ │ ├── oidcprovider.md
│ │ │ │ ├── policy.md
│ │ │ │ ├── role-policy.md
│ │ │ │ ├── role.md
│ │ │ │ ├── samlprovider.md
│ │ │ │ ├── server-certificate.md
│ │ │ │ ├── service-linked-role.md
│ │ │ │ ├── user-policy.md
│ │ │ │ ├── user-to-group-addition.md
│ │ │ │ ├── user.md
│ │ │ │ └── virtual-mfadevice.md
│ │ │ ├── identity-store
│ │ │ │ ├── group-membership.md
│ │ │ │ └── group.md
│ │ │ ├── image-builder
│ │ │ │ ├── component.md
│ │ │ │ ├── container-recipe.md
│ │ │ │ ├── distribution-configuration.md
│ │ │ │ ├── image-pipeline.md
│ │ │ │ ├── image-recipe.md
│ │ │ │ ├── image.md
│ │ │ │ ├── infrastructure-configuration.md
│ │ │ │ ├── lifecycle-policy.md
│ │ │ │ └── workflow.md
│ │ │ ├── inspector-v2
│ │ │ │ ├── cis-scan-configuration.md
│ │ │ │ └── filter.md
│ │ │ ├── inspector
│ │ │ │ ├── assessment-target.md
│ │ │ │ ├── assessment-template.md
│ │ │ │ └── resource-group.md
│ │ │ ├── internet-monitor
│ │ │ │ └── monitor.md
│ │ │ ├── invoicing
│ │ │ │ └── invoice-unit.md
│ │ │ ├── io-t
│ │ │ │ ├── account-audit-configuration.md
│ │ │ │ ├── authorizer.md
│ │ │ │ ├── billing-group.md
│ │ │ │ ├── cacertificate.md
│ │ │ │ ├── certificate-provider.md
│ │ │ │ ├── certificate.md
│ │ │ │ ├── command.md
│ │ │ │ ├── custom-metric.md
│ │ │ │ ├── dimension.md
│ │ │ │ ├── domain-configuration.md
│ │ │ │ ├── fleet-metric.md
│ │ │ │ ├── job-template.md
│ │ │ │ ├── logging.md
│ │ │ │ ├── mitigation-action.md
│ │ │ │ ├── policy-principal-attachment.md
│ │ │ │ ├── policy.md
│ │ │ │ ├── provisioning-template.md
│ │ │ │ ├── resource-specific-logging.md
│ │ │ │ ├── role-alias.md
│ │ │ │ ├── scheduled-audit.md
│ │ │ │ ├── security-profile.md
│ │ │ │ ├── software-package-version.md
│ │ │ │ ├── software-package.md
│ │ │ │ ├── thing-group.md
│ │ │ │ ├── thing-principal-attachment.md
│ │ │ │ ├── thing-type.md
│ │ │ │ ├── thing.md
│ │ │ │ ├── topic-rule-destination.md
│ │ │ │ └── topic-rule.md
│ │ │ ├── io-tanalytics
│ │ │ │ ├── channel.md
│ │ │ │ ├── dataset.md
│ │ │ │ ├── datastore.md
│ │ │ │ └── pipeline.md
│ │ │ ├── io-tcore-device-advisor
│ │ │ │ └── suite-definition.md
│ │ │ ├── io-tevents
│ │ │ │ ├── alarm-model.md
│ │ │ │ ├── detector-model.md
│ │ │ │ └── input.md
│ │ │ ├── io-tfleet-hub
│ │ │ │ └── application.md
│ │ │ ├── io-tfleet-wise
│ │ │ │ ├── campaign.md
│ │ │ │ ├── decoder-manifest.md
│ │ │ │ ├── fleet.md
│ │ │ │ ├── model-manifest.md
│ │ │ │ ├── signal-catalog.md
│ │ │ │ ├── state-template.md
│ │ │ │ └── vehicle.md
│ │ │ ├── io-tsite-wise
│ │ │ │ ├── access-policy.md
│ │ │ │ ├── asset-model.md
│ │ │ │ ├── asset.md
│ │ │ │ ├── dashboard.md
│ │ │ │ ├── dataset.md
│ │ │ │ ├── gateway.md
│ │ │ │ ├── portal.md
│ │ │ │ └── project.md
│ │ │ ├── io-tthings-graph
│ │ │ │ └── flow-template.md
│ │ │ ├── io-ttwin-maker
│ │ │ │ ├── component-type.md
│ │ │ │ ├── entity.md
│ │ │ │ ├── scene.md
│ │ │ │ ├── sync-job.md
│ │ │ │ └── workspace.md
│ │ │ ├── io-twireless
│ │ │ │ ├── destination.md
│ │ │ │ ├── device-profile.md
│ │ │ │ ├── fuota-task.md
│ │ │ │ ├── multicast-group.md
│ │ │ │ ├── network-analyzer-configuration.md
│ │ │ │ ├── partner-account.md
│ │ │ │ ├── service-profile.md
│ │ │ │ ├── task-definition.md
│ │ │ │ ├── wireless-device-import-task.md
│ │ │ │ ├── wireless-device.md
│ │ │ │ └── wireless-gateway.md
│ │ │ ├── ivs
│ │ │ │ ├── channel.md
│ │ │ │ ├── encoder-configuration.md
│ │ │ │ ├── ingest-configuration.md
│ │ │ │ ├── playback-key-pair.md
│ │ │ │ ├── playback-restriction-policy.md
│ │ │ │ ├── public-key.md
│ │ │ │ ├── recording-configuration.md
│ │ │ │ ├── stage.md
│ │ │ │ ├── storage-configuration.md
│ │ │ │ └── stream-key.md
│ │ │ ├── ivschat
│ │ │ │ ├── logging-configuration.md
│ │ │ │ └── room.md
│ │ │ ├── kafka-connect
│ │ │ │ ├── connector.md
│ │ │ │ ├── custom-plugin.md
│ │ │ │ └── worker-configuration.md
│ │ │ ├── kendra-ranking
│ │ │ │ └── execution-plan.md
│ │ │ ├── kendra
│ │ │ │ ├── data-source.md
│ │ │ │ ├── faq.md
│ │ │ │ └── index.md
│ │ │ ├── kinesis-analytics-v2
│ │ │ │ ├── application-cloud-watch-logging-option.md
│ │ │ │ ├── application-output.md
│ │ │ │ ├── application-reference-data-source.md
│ │ │ │ └── application.md
│ │ │ ├── kinesis-analytics
│ │ │ │ ├── application-output.md
│ │ │ │ ├── application-reference-data-source.md
│ │ │ │ └── application.md
│ │ │ ├── kinesis-firehose
│ │ │ │ └── delivery-stream.md
│ │ │ ├── kinesis-video
│ │ │ │ ├── signaling-channel.md
│ │ │ │ └── stream.md
│ │ │ ├── kinesis
│ │ │ │ ├── resource-policy.md
│ │ │ │ ├── stream-consumer.md
│ │ │ │ └── stream.md
│ │ │ ├── kms
│ │ │ │ ├── alias.md
│ │ │ │ ├── key.md
│ │ │ │ └── replica-key.md
│ │ │ ├── lake-formation
│ │ │ │ ├── data-cells-filter.md
│ │ │ │ ├── data-lake-settings.md
│ │ │ │ ├── permissions.md
│ │ │ │ ├── principal-permissions.md
│ │ │ │ ├── resource.md
│ │ │ │ ├── tag-association.md
│ │ │ │ └── tag.md
│ │ │ ├── lambda
│ │ │ │ ├── alias.md
│ │ │ │ ├── code-signing-config.md
│ │ │ │ ├── event-invoke-config.md
│ │ │ │ ├── event-source-mapping.md
│ │ │ │ ├── function.md
│ │ │ │ ├── layer-version-permission.md
│ │ │ │ ├── layer-version.md
│ │ │ │ ├── permission.md
│ │ │ │ ├── url.md
│ │ │ │ └── version.md
│ │ │ ├── launch-wizard
│ │ │ │ └── deployment.md
│ │ │ ├── lex
│ │ │ │ ├── bot-alias.md
│ │ │ │ ├── bot-version.md
│ │ │ │ ├── bot.md
│ │ │ │ └── resource-policy.md
│ │ │ ├── license-manager
│ │ │ │ ├── grant.md
│ │ │ │ └── license.md
│ │ │ ├── lightsail
│ │ │ │ ├── alarm.md
│ │ │ │ ├── bucket.md
│ │ │ │ ├── certificate.md
│ │ │ │ ├── container.md
│ │ │ │ ├── database.md
│ │ │ │ ├── disk.md
│ │ │ │ ├── distribution.md
│ │ │ │ ├── instance.md
│ │ │ │ ├── load-balancer-tls-certificate.md
│ │ │ │ ├── load-balancer.md
│ │ │ │ └── static-ip.md
│ │ │ ├── location
│ │ │ │ ├── apikey.md
│ │ │ │ ├── geofence-collection.md
│ │ │ │ ├── map.md
│ │ │ │ ├── place-index.md
│ │ │ │ ├── route-calculator.md
│ │ │ │ ├── tracker-consumer.md
│ │ │ │ └── tracker.md
│ │ │ ├── logs
│ │ │ │ ├── account-policy.md
│ │ │ │ ├── delivery-destination.md
│ │ │ │ ├── delivery-source.md
│ │ │ │ ├── delivery.md
│ │ │ │ ├── destination.md
│ │ │ │ ├── integration.md
│ │ │ │ ├── log-anomaly-detector.md
│ │ │ │ ├── log-group.md
│ │ │ │ ├── log-stream.md
│ │ │ │ ├── metric-filter.md
│ │ │ │ ├── query-definition.md
│ │ │ │ ├── resource-policy.md
│ │ │ │ ├── subscription-filter.md
│ │ │ │ └── transformer.md
│ │ │ ├── lookout-equipment
│ │ │ │ └── inference-scheduler.md
│ │ │ ├── lookout-metrics
│ │ │ │ ├── alert.md
│ │ │ │ └── anomaly-detector.md
│ │ │ ├── lookout-vision
│ │ │ │ └── project.md
│ │ │ ├── m2
│ │ │ │ ├── application.md
│ │ │ │ ├── deployment.md
│ │ │ │ └── environment.md
│ │ │ ├── macie
│ │ │ │ ├── allow-list.md
│ │ │ │ ├── custom-data-identifier.md
│ │ │ │ ├── findings-filter.md
│ │ │ │ └── session.md
│ │ │ ├── managed-blockchain
│ │ │ │ ├── accessor.md
│ │ │ │ ├── member.md
│ │ │ │ └── node.md
│ │ │ ├── media-connect
│ │ │ │ ├── bridge-output.md
│ │ │ │ ├── bridge-source.md
│ │ │ │ ├── bridge.md
│ │ │ │ ├── flow-entitlement.md
│ │ │ │ ├── flow-output.md
│ │ │ │ ├── flow-source.md
│ │ │ │ ├── flow-vpc-interface.md
│ │ │ │ ├── flow.md
│ │ │ │ └── gateway.md
│ │ │ ├── media-convert
│ │ │ │ ├── job-template.md
│ │ │ │ ├── preset.md
│ │ │ │ └── queue.md
│ │ │ ├── media-live
│ │ │ │ ├── channel-placement-group.md
│ │ │ │ ├── channel.md
│ │ │ │ ├── cloud-watch-alarm-template-group.md
│ │ │ │ ├── cloud-watch-alarm-template.md
│ │ │ │ ├── cluster.md
│ │ │ │ ├── event-bridge-rule-template-group.md
│ │ │ │ ├── event-bridge-rule-template.md
│ │ │ │ ├── input-security-group.md
│ │ │ │ ├── input.md
│ │ │ │ ├── multiplex.md
│ │ │ │ ├── multiplexprogram.md
│ │ │ │ ├── network.md
│ │ │ │ ├── sdi-source.md
│ │ │ │ └── signal-map.md
│ │ │ ├── media-package-v2
│ │ │ │ ├── channel-group.md
│ │ │ │ ├── channel-policy.md
│ │ │ │ ├── channel.md
│ │ │ │ ├── origin-endpoint-policy.md
│ │ │ │ └── origin-endpoint.md
│ │ │ ├── media-package
│ │ │ │ ├── asset.md
│ │ │ │ ├── channel.md
│ │ │ │ ├── origin-endpoint.md
│ │ │ │ ├── packaging-configuration.md
│ │ │ │ └── packaging-group.md
│ │ │ ├── media-store
│ │ │ │ └── container.md
│ │ │ ├── media-tailor
│ │ │ │ ├── channel-policy.md
│ │ │ │ ├── channel.md
│ │ │ │ ├── live-source.md
│ │ │ │ ├── playback-configuration.md
│ │ │ │ ├── source-location.md
│ │ │ │ └── vod-source.md
│ │ │ ├── memory-db
│ │ │ │ ├── acl.md
│ │ │ │ ├── cluster.md
│ │ │ │ ├── multi-region-cluster.md
│ │ │ │ ├── parameter-group.md
│ │ │ │ ├── subnet-group.md
│ │ │ │ └── user.md
│ │ │ ├── msk
│ │ │ │ ├── batch-scram-secret.md
│ │ │ │ ├── cluster-policy.md
│ │ │ │ ├── cluster.md
│ │ │ │ ├── configuration.md
│ │ │ │ ├── replicator.md
│ │ │ │ ├── serverless-cluster.md
│ │ │ │ └── vpc-connection.md
│ │ │ ├── mwaa
│ │ │ │ └── environment.md
│ │ │ ├── neptune-graph
│ │ │ │ ├── graph.md
│ │ │ │ └── private-graph-endpoint.md
│ │ │ ├── neptune
│ │ │ │ ├── dbcluster-parameter-group.md
│ │ │ │ ├── dbcluster.md
│ │ │ │ ├── dbinstance.md
│ │ │ │ ├── dbparameter-group.md
│ │ │ │ ├── dbsubnet-group.md
│ │ │ │ └── event-subscription.md
│ │ │ ├── network-firewall
│ │ │ │ ├── firewall-policy.md
│ │ │ │ ├── firewall.md
│ │ │ │ ├── logging-configuration.md
│ │ │ │ ├── rule-group.md
│ │ │ │ └── tlsinspection-configuration.md
│ │ │ ├── network-manager
│ │ │ │ ├── connect-attachment.md
│ │ │ │ ├── connect-peer.md
│ │ │ │ ├── core-network.md
│ │ │ │ ├── customer-gateway-association.md
│ │ │ │ ├── device.md
│ │ │ │ ├── direct-connect-gateway-attachment.md
│ │ │ │ ├── global-network.md
│ │ │ │ ├── link-association.md
│ │ │ │ ├── link.md
│ │ │ │ ├── site-to-site-vpn-attachment.md
│ │ │ │ ├── site.md
│ │ │ │ ├── transit-gateway-peering.md
│ │ │ │ ├── transit-gateway-registration.md
│ │ │ │ ├── transit-gateway-route-table-attachment.md
│ │ │ │ └── vpc-attachment.md
│ │ │ ├── notifications-contacts
│ │ │ │ └── email-contact.md
│ │ │ ├── notifications
│ │ │ │ ├── channel-association.md
│ │ │ │ ├── event-rule.md
│ │ │ │ ├── managed-notification-account-contact-association.md
│ │ │ │ ├── managed-notification-additional-channel-association.md
│ │ │ │ ├── notification-configuration.md
│ │ │ │ └── notification-hub.md
│ │ │ ├── oam
│ │ │ │ ├── link.md
│ │ │ │ └── sink.md
│ │ │ ├── omics
│ │ │ │ ├── annotation-store.md
│ │ │ │ ├── reference-store.md
│ │ │ │ ├── run-group.md
│ │ │ │ ├── sequence-store.md
│ │ │ │ ├── variant-store.md
│ │ │ │ └── workflow.md
│ │ │ ├── open-search-serverless
│ │ │ │ ├── access-policy.md
│ │ │ │ ├── collection.md
│ │ │ │ ├── index.md
│ │ │ │ ├── lifecycle-policy.md
│ │ │ │ ├── security-config.md
│ │ │ │ ├── security-policy.md
│ │ │ │ └── vpc-endpoint.md
│ │ │ ├── open-search-service
│ │ │ │ ├── application.md
│ │ │ │ └── domain.md
│ │ │ ├── ops-works-cm
│ │ │ │ └── server.md
│ │ │ ├── ops-works
│ │ │ │ ├── app.md
│ │ │ │ ├── elastic-load-balancer-attachment.md
│ │ │ │ ├── instance.md
│ │ │ │ ├── layer.md
│ │ │ │ ├── stack.md
│ │ │ │ ├── user-profile.md
│ │ │ │ └── volume.md
│ │ │ ├── organizations
│ │ │ │ ├── account.md
│ │ │ │ ├── organization.md
│ │ │ │ ├── organizational-unit.md
│ │ │ │ ├── policy.md
│ │ │ │ └── resource-policy.md
│ │ │ ├── osis
│ │ │ │ └── pipeline.md
│ │ │ ├── panorama
│ │ │ │ ├── application-instance.md
│ │ │ │ ├── package-version.md
│ │ │ │ └── package.md
│ │ │ ├── payment-cryptography
│ │ │ │ ├── alias.md
│ │ │ │ └── key.md
│ │ │ ├── pcaconnector-ad
│ │ │ │ ├── connector.md
│ │ │ │ ├── directory-registration.md
│ │ │ │ ├── service-principal-name.md
│ │ │ │ ├── template-group-access-control-entry.md
│ │ │ │ └── template.md
│ │ │ ├── pcaconnector-scep
│ │ │ │ ├── challenge.md
│ │ │ │ └── connector.md
│ │ │ ├── pcs
│ │ │ │ ├── cluster.md
│ │ │ │ ├── compute-node-group.md
│ │ │ │ └── queue.md
│ │ │ ├── personalize
│ │ │ │ ├── dataset-group.md
│ │ │ │ ├── dataset.md
│ │ │ │ ├── schema.md
│ │ │ │ └── solution.md
│ │ │ ├── pinpoint-email
│ │ │ │ ├── configuration-set-event-destination.md
│ │ │ │ ├── configuration-set.md
│ │ │ │ ├── dedicated-ip-pool.md
│ │ │ │ └── identity.md
│ │ │ ├── pinpoint
│ │ │ │ ├── admchannel.md
│ │ │ │ ├── apnschannel.md
│ │ │ │ ├── apnssandbox-channel.md
│ │ │ │ ├── apnsvoip-channel.md
│ │ │ │ ├── apnsvoip-sandbox-channel.md
│ │ │ │ ├── app.md
│ │ │ │ ├── application-settings.md
│ │ │ │ ├── baidu-channel.md
│ │ │ │ ├── campaign.md
│ │ │ │ ├── email-channel.md
│ │ │ │ ├── email-template.md
│ │ │ │ ├── event-stream.md
│ │ │ │ ├── gcmchannel.md
│ │ │ │ ├── in-app-template.md
│ │ │ │ ├── push-template.md
│ │ │ │ ├── segment.md
│ │ │ │ ├── sms-template.md
│ │ │ │ ├── smschannel.md
│ │ │ │ └── voice-channel.md
│ │ │ ├── pipes
│ │ │ │ └── pipe.md
│ │ │ ├── proton
│ │ │ │ ├── environment-account-connection.md
│ │ │ │ ├── environment-template.md
│ │ │ │ └── service-template.md
│ │ │ ├── qbusiness
│ │ │ │ ├── application.md
│ │ │ │ ├── data-accessor.md
│ │ │ │ ├── data-source.md
│ │ │ │ ├── index.md
│ │ │ │ ├── permission.md
│ │ │ │ ├── plugin.md
│ │ │ │ ├── retriever.md
│ │ │ │ └── web-experience.md
│ │ │ ├── qldb
│ │ │ │ ├── ledger.md
│ │ │ │ └── stream.md
│ │ │ ├── quick-sight
│ │ │ │ ├── analysis.md
│ │ │ │ ├── custom-permissions.md
│ │ │ │ ├── dashboard.md
│ │ │ │ ├── data-set.md
│ │ │ │ ├── data-source.md
│ │ │ │ ├── folder.md
│ │ │ │ ├── refresh-schedule.md
│ │ │ │ ├── template.md
│ │ │ │ ├── theme.md
│ │ │ │ ├── topic.md
│ │ │ │ └── vpcconnection.md
│ │ │ ├── ram
│ │ │ │ ├── permission.md
│ │ │ │ └── resource-share.md
│ │ │ ├── rbin
│ │ │ │ └── rule.md
│ │ │ ├── rds
│ │ │ │ ├── custom-dbengine-version.md
│ │ │ │ ├── dbcluster-parameter-group.md
│ │ │ │ ├── dbcluster.md
│ │ │ │ ├── dbinstance.md
│ │ │ │ ├── dbparameter-group.md
│ │ │ │ ├── dbproxy-endpoint.md
│ │ │ │ ├── dbproxy-target-group.md
│ │ │ │ ├── dbproxy.md
│ │ │ │ ├── dbsecurity-group-ingress.md
│ │ │ │ ├── dbsecurity-group.md
│ │ │ │ ├── dbshard-group.md
│ │ │ │ ├── dbsubnet-group.md
│ │ │ │ ├── event-subscription.md
│ │ │ │ ├── global-cluster.md
│ │ │ │ ├── integration.md
│ │ │ │ └── option-group.md
│ │ │ ├── redshift-serverless
│ │ │ │ ├── namespace.md
│ │ │ │ └── workgroup.md
│ │ │ ├── redshift
│ │ │ │ ├── cluster-parameter-group.md
│ │ │ │ ├── cluster-security-group-ingress.md
│ │ │ │ ├── cluster-security-group.md
│ │ │ │ ├── cluster-subnet-group.md
│ │ │ │ ├── cluster.md
│ │ │ │ ├── endpoint-access.md
│ │ │ │ ├── endpoint-authorization.md
│ │ │ │ ├── event-subscription.md
│ │ │ │ ├── integration.md
│ │ │ │ └── scheduled-action.md
│ │ │ ├── refactor-spaces
│ │ │ │ ├── application.md
│ │ │ │ ├── environment.md
│ │ │ │ ├── route.md
│ │ │ │ └── service.md
│ │ │ ├── rekognition
│ │ │ │ ├── collection.md
│ │ │ │ ├── project.md
│ │ │ │ └── stream-processor.md
│ │ │ ├── resilience-hub
│ │ │ │ ├── app.md
│ │ │ │ └── resiliency-policy.md
│ │ │ ├── resource-explorer2
│ │ │ │ ├── default-view-association.md
│ │ │ │ ├── index.md
│ │ │ │ └── view.md
│ │ │ ├── resource-groups
│ │ │ │ ├── group.md
│ │ │ │ └── tag-sync-task.md
│ │ │ ├── robo-maker
│ │ │ │ ├── fleet.md
│ │ │ │ ├── robot-application-version.md
│ │ │ │ ├── robot-application.md
│ │ │ │ ├── robot.md
│ │ │ │ ├── simulation-application-version.md
│ │ │ │ └── simulation-application.md
│ │ │ ├── roles-anywhere
│ │ │ │ ├── crl.md
│ │ │ │ ├── profile.md
│ │ │ │ └── trust-anchor.md
│ │ │ ├── route53
│ │ │ │ ├── cidr-collection.md
│ │ │ │ ├── dnssec.md
│ │ │ │ ├── health-check.md
│ │ │ │ ├── hosted-zone.md
│ │ │ │ ├── key-signing-key.md
│ │ │ │ ├── record-set-group.md
│ │ │ │ └── record-set.md
│ │ │ ├── route53profiles
│ │ │ │ ├── profile-association.md
│ │ │ │ ├── profile-resource-association.md
│ │ │ │ └── profile.md
│ │ │ ├── route53recovery-control
│ │ │ │ ├── cluster.md
│ │ │ │ ├── control-panel.md
│ │ │ │ ├── routing-control.md
│ │ │ │ └── safety-rule.md
│ │ │ ├── route53recovery-readiness
│ │ │ │ ├── cell.md
│ │ │ │ ├── readiness-check.md
│ │ │ │ ├── recovery-group.md
│ │ │ │ └── resource-set.md
│ │ │ ├── route53resolver
│ │ │ │ ├── firewall-domain-list.md
│ │ │ │ ├── firewall-rule-group-association.md
│ │ │ │ ├── firewall-rule-group.md
│ │ │ │ ├── outpost-resolver.md
│ │ │ │ ├── resolver-config.md
│ │ │ │ ├── resolver-dnssecconfig.md
│ │ │ │ ├── resolver-endpoint.md
│ │ │ │ ├── resolver-query-logging-config-association.md
│ │ │ │ ├── resolver-query-logging-config.md
│ │ │ │ ├── resolver-rule-association.md
│ │ │ │ └── resolver-rule.md
│ │ │ ├── rum
│ │ │ │ └── app-monitor.md
│ │ │ ├── s3
│ │ │ │ ├── access-grant.md
│ │ │ │ ├── access-grants-instance.md
│ │ │ │ ├── access-grants-location.md
│ │ │ │ ├── access-point.md
│ │ │ │ ├── bucket-policy.md
│ │ │ │ ├── bucket.md
│ │ │ │ ├── multi-region-access-point-policy.md
│ │ │ │ ├── multi-region-access-point.md
│ │ │ │ ├── storage-lens-group.md
│ │ │ │ └── storage-lens.md
│ │ │ ├── s3express
│ │ │ │ ├── bucket-policy.md
│ │ │ │ └── directory-bucket.md
│ │ │ ├── s3object-lambda
│ │ │ │ ├── access-point-policy.md
│ │ │ │ └── access-point.md
│ │ │ ├── s3outposts
│ │ │ │ ├── access-point.md
│ │ │ │ ├── bucket-policy.md
│ │ │ │ ├── bucket.md
│ │ │ │ └── endpoint.md
│ │ │ ├── s3tables
│ │ │ │ ├── table-bucket-policy.md
│ │ │ │ └── table-bucket.md
│ │ │ ├── sage-maker
│ │ │ │ ├── app-image-config.md
│ │ │ │ ├── app.md
│ │ │ │ ├── cluster.md
│ │ │ │ ├── code-repository.md
│ │ │ │ ├── data-quality-job-definition.md
│ │ │ │ ├── device-fleet.md
│ │ │ │ ├── device.md
│ │ │ │ ├── domain.md
│ │ │ │ ├── endpoint-config.md
│ │ │ │ ├── endpoint.md
│ │ │ │ ├── feature-group.md
│ │ │ │ ├── image-version.md
│ │ │ │ ├── image.md
│ │ │ │ ├── inference-component.md
│ │ │ │ ├── inference-experiment.md
│ │ │ │ ├── mlflow-tracking-server.md
│ │ │ │ ├── model-bias-job-definition.md
│ │ │ │ ├── model-card.md
│ │ │ │ ├── model-explainability-job-definition.md
│ │ │ │ ├── model-package-group.md
│ │ │ │ ├── model-package.md
│ │ │ │ ├── model-quality-job-definition.md
│ │ │ │ ├── model.md
│ │ │ │ ├── monitoring-schedule.md
│ │ │ │ ├── notebook-instance-lifecycle-config.md
│ │ │ │ ├── notebook-instance.md
│ │ │ │ ├── partner-app.md
│ │ │ │ ├── pipeline.md
│ │ │ │ ├── project.md
│ │ │ │ ├── space.md
│ │ │ │ ├── studio-lifecycle-config.md
│ │ │ │ ├── user-profile.md
│ │ │ │ └── workteam.md
│ │ │ ├── scheduler
│ │ │ │ ├── schedule-group.md
│ │ │ │ └── schedule.md
│ │ │ ├── sdb
│ │ │ │ └── domain.md
│ │ │ ├── secrets-manager
│ │ │ │ ├── resource-policy.md
│ │ │ │ ├── rotation-schedule.md
│ │ │ │ ├── secret-target-attachment.md
│ │ │ │ └── secret.md
│ │ │ ├── security-hub
│ │ │ │ ├── automation-rule.md
│ │ │ │ ├── configuration-policy.md
│ │ │ │ ├── delegated-admin.md
│ │ │ │ ├── finding-aggregator.md
│ │ │ │ ├── hub.md
│ │ │ │ ├── insight.md
│ │ │ │ ├── organization-configuration.md
│ │ │ │ ├── policy-association.md
│ │ │ │ ├── product-subscription.md
│ │ │ │ ├── security-control.md
│ │ │ │ └── standard.md
│ │ │ ├── security-lake
│ │ │ │ ├── aws-log-source.md
│ │ │ │ ├── data-lake.md
│ │ │ │ ├── subscriber-notification.md
│ │ │ │ └── subscriber.md
│ │ │ ├── service-catalog-app-registry
│ │ │ │ ├── application.md
│ │ │ │ ├── attribute-group-association.md
│ │ │ │ ├── attribute-group.md
│ │ │ │ └── resource-association.md
│ │ │ ├── service-catalog
│ │ │ │ ├── accepted-portfolio-share.md
│ │ │ │ ├── cloud-formation-product.md
│ │ │ │ ├── cloud-formation-provisioned-product.md
│ │ │ │ ├── launch-notification-constraint.md
│ │ │ │ ├── launch-role-constraint.md
│ │ │ │ ├── launch-template-constraint.md
│ │ │ │ ├── portfolio-principal-association.md
│ │ │ │ ├── portfolio-product-association.md
│ │ │ │ ├── portfolio-share.md
│ │ │ │ ├── portfolio.md
│ │ │ │ ├── resource-update-constraint.md
│ │ │ │ ├── service-action-association.md
│ │ │ │ ├── service-action.md
│ │ │ │ ├── stack-set-constraint.md
│ │ │ │ ├── tag-option-association.md
│ │ │ │ └── tag-option.md
│ │ │ ├── service-discovery
│ │ │ │ ├── http-namespace.md
│ │ │ │ ├── instance.md
│ │ │ │ ├── private-dns-namespace.md
│ │ │ │ ├── public-dns-namespace.md
│ │ │ │ └── service.md
│ │ │ ├── ses
│ │ │ │ ├── configuration-set-event-destination.md
│ │ │ │ ├── configuration-set.md
│ │ │ │ ├── contact-list.md
│ │ │ │ ├── dedicated-ip-pool.md
│ │ │ │ ├── email-identity.md
│ │ │ │ ├── mail-manager-addon-instance.md
│ │ │ │ ├── mail-manager-addon-subscription.md
│ │ │ │ ├── mail-manager-archive.md
│ │ │ │ ├── mail-manager-ingress-point.md
│ │ │ │ ├── mail-manager-relay.md
│ │ │ │ ├── mail-manager-rule-set.md
│ │ │ │ ├── mail-manager-traffic-policy.md
│ │ │ │ ├── receipt-filter.md
│ │ │ │ ├── receipt-rule-set.md
│ │ │ │ ├── receipt-rule.md
│ │ │ │ ├── template.md
│ │ │ │ └── vdm-attributes.md
│ │ │ ├── shield
│ │ │ │ ├── drtaccess.md
│ │ │ │ ├── proactive-engagement.md
│ │ │ │ ├── protection-group.md
│ │ │ │ └── protection.md
│ │ │ ├── signer
│ │ │ │ ├── profile-permission.md
│ │ │ │ └── signing-profile.md
│ │ │ ├── sim-space-weaver
│ │ │ │ └── simulation.md
│ │ │ ├── sns
│ │ │ │ ├── subscription.md
│ │ │ │ ├── topic-inline-policy.md
│ │ │ │ ├── topic-policy.md
│ │ │ │ └── topic.md
│ │ │ ├── sqs
│ │ │ │ ├── queue-inline-policy.md
│ │ │ │ ├── queue-policy.md
│ │ │ │ └── queue.md
│ │ │ ├── ssm
│ │ │ │ ├── association.md
│ │ │ │ ├── document.md
│ │ │ │ ├── maintenance-window-target.md
│ │ │ │ ├── maintenance-window-task.md
│ │ │ │ ├── maintenance-window.md
│ │ │ │ ├── parameter.md
│ │ │ │ ├── patch-baseline.md
│ │ │ │ ├── resource-data-sync.md
│ │ │ │ └── resource-policy.md
│ │ │ ├── ssmcontacts
│ │ │ │ ├── contact-channel.md
│ │ │ │ ├── contact.md
│ │ │ │ ├── plan.md
│ │ │ │ └── rotation.md
│ │ │ ├── ssmincidents
│ │ │ │ ├── replication-set.md
│ │ │ │ └── response-plan.md
│ │ │ ├── ssmquick-setup
│ │ │ │ └── configuration-manager.md
│ │ │ ├── sso
│ │ │ │ ├── application-assignment.md
│ │ │ │ ├── application.md
│ │ │ │ ├── assignment.md
│ │ │ │ ├── instance-access-control-attribute-configuration.md
│ │ │ │ ├── instance.md
│ │ │ │ └── permission-set.md
│ │ │ ├── step-functions
│ │ │ │ ├── activity.md
│ │ │ │ ├── state-machine-alias.md
│ │ │ │ ├── state-machine-version.md
│ │ │ │ └── state-machine.md
│ │ │ ├── support-app
│ │ │ │ ├── account-alias.md
│ │ │ │ ├── slack-channel-configuration.md
│ │ │ │ └── slack-workspace-configuration.md
│ │ │ ├── synthetics
│ │ │ │ ├── canary.md
│ │ │ │ └── group.md
│ │ │ ├── systems-manager-sap
│ │ │ │ └── application.md
│ │ │ ├── timestream
│ │ │ │ ├── database.md
│ │ │ │ ├── influx-dbinstance.md
│ │ │ │ ├── scheduled-query.md
│ │ │ │ └── table.md
│ │ │ ├── transfer
│ │ │ │ ├── agreement.md
│ │ │ │ ├── certificate.md
│ │ │ │ ├── connector.md
│ │ │ │ ├── profile.md
│ │ │ │ ├── server.md
│ │ │ │ ├── user.md
│ │ │ │ ├── web-app.md
│ │ │ │ └── workflow.md
│ │ │ ├── verified-permissions
│ │ │ │ ├── identity-source.md
│ │ │ │ ├── policy-store.md
│ │ │ │ ├── policy-template.md
│ │ │ │ └── policy.md
│ │ │ ├── voice-id
│ │ │ │ └── domain.md
│ │ │ ├── vpc-lattice
│ │ │ │ ├── access-log-subscription.md
│ │ │ │ ├── auth-policy.md
│ │ │ │ ├── listener.md
│ │ │ │ ├── resource-configuration.md
│ │ │ │ ├── resource-gateway.md
│ │ │ │ ├── resource-policy.md
│ │ │ │ ├── rule.md
│ │ │ │ ├── service-network-resource-association.md
│ │ │ │ ├── service-network-service-association.md
│ │ │ │ ├── service-network-vpc-association.md
│ │ │ │ ├── service-network.md
│ │ │ │ ├── service.md
│ │ │ │ └── target-group.md
│ │ │ ├── waf
│ │ │ │ ├── byte-match-set.md
│ │ │ │ ├── ipset.md
│ │ │ │ ├── rule.md
│ │ │ │ ├── size-constraint-set.md
│ │ │ │ ├── sql-injection-match-set.md
│ │ │ │ ├── web-acl.md
│ │ │ │ └── xss-match-set.md
│ │ │ ├── wafregional
│ │ │ │ ├── byte-match-set.md
│ │ │ │ ├── geo-match-set.md
│ │ │ │ ├── ipset.md
│ │ │ │ ├── rate-based-rule.md
│ │ │ │ ├── regex-pattern-set.md
│ │ │ │ ├── rule.md
│ │ │ │ ├── size-constraint-set.md
│ │ │ │ ├── sql-injection-match-set.md
│ │ │ │ ├── web-acl.md
│ │ │ │ ├── web-aclassociation.md
│ │ │ │ └── xss-match-set.md
│ │ │ ├── wafv2
│ │ │ │ ├── ipset.md
│ │ │ │ ├── logging-configuration.md
│ │ │ │ ├── regex-pattern-set.md
│ │ │ │ ├── rule-group.md
│ │ │ │ ├── web-acl.md
│ │ │ │ └── web-aclassociation.md
│ │ │ ├── wisdom
│ │ │ │ ├── aiagent-version.md
│ │ │ │ ├── aiagent.md
│ │ │ │ ├── aiguardrail-version.md
│ │ │ │ ├── aiguardrail.md
│ │ │ │ ├── aiprompt-version.md
│ │ │ │ ├── aiprompt.md
│ │ │ │ ├── assistant-association.md
│ │ │ │ ├── assistant.md
│ │ │ │ ├── knowledge-base.md
│ │ │ │ ├── message-template-version.md
│ │ │ │ └── message-template.md
│ │ │ ├── work-spaces-thin-client
│ │ │ │ └── environment.md
│ │ │ ├── work-spaces-web
│ │ │ │ ├── browser-settings.md
│ │ │ │ ├── data-protection-settings.md
│ │ │ │ ├── identity-provider.md
│ │ │ │ ├── ip-access-settings.md
│ │ │ │ ├── network-settings.md
│ │ │ │ ├── portal.md
│ │ │ │ ├── trust-store.md
│ │ │ │ ├── user-access-logging-settings.md
│ │ │ │ └── user-settings.md
│ │ │ ├── work-spaces
│ │ │ │ ├── connection-alias.md
│ │ │ │ ├── workspace.md
│ │ │ │ └── workspaces-pool.md
│ │ │ └── xray
│ │ │ │ ├── group.md
│ │ │ │ ├── resource-policy.md
│ │ │ │ ├── sampling-rule.md
│ │ │ │ └── transaction-search-config.md
│ │ ├── aws
│ │ │ ├── bucket.md
│ │ │ ├── control.md
│ │ │ ├── function.md
│ │ │ ├── policy-attachment.md
│ │ │ ├── policy.md
│ │ │ ├── queue.md
│ │ │ ├── role.md
│ │ │ ├── ses.md
│ │ │ └── table.md
│ │ ├── cloudflare
│ │ │ ├── account-api-token.md
│ │ │ ├── account-id.md
│ │ │ ├── ai-gateway.md
│ │ │ ├── assets.md
│ │ │ ├── bucket.md
│ │ │ ├── custom-domain.md
│ │ │ ├── d1-database.md
│ │ │ ├── dns-records.md
│ │ │ ├── durable-object-namespace.md
│ │ │ ├── hyperdrive.md
│ │ │ ├── kv-namespace.md
│ │ │ ├── nuxt.md
│ │ │ ├── permission-groups.md
│ │ │ ├── pipeline.md
│ │ │ ├── queue-consumer.md
│ │ │ ├── queue.md
│ │ │ ├── redwood.md
│ │ │ ├── route.md
│ │ │ ├── tanstack-start.md
│ │ │ ├── vite.md
│ │ │ ├── website.md
│ │ │ ├── worker.md
│ │ │ ├── workflow.md
│ │ │ ├── wrangler.json.md
│ │ │ └── zone.md
│ │ ├── dns
│ │ │ └── import-dns.md
│ │ ├── esbuild
│ │ │ └── bundle.md
│ │ ├── fs
│ │ │ ├── copy-file.md
│ │ │ ├── file.md
│ │ │ ├── folder.md
│ │ │ ├── static-astro-file.md
│ │ │ ├── static-css-file.md
│ │ │ ├── static-html-file.md
│ │ │ ├── static-json-file.md
│ │ │ ├── static-text-file.md
│ │ │ ├── static-typescript-file.md
│ │ │ ├── static-vue-file.md
│ │ │ └── static-yaml-file.md
│ │ ├── github
│ │ │ ├── repository-environment.md
│ │ │ └── secret.md
│ │ ├── neon
│ │ │ └── project.md
│ │ ├── os
│ │ │ └── exec.md
│ │ ├── sentry
│ │ │ ├── client-key.md
│ │ │ ├── project.md
│ │ │ └── team.md
│ │ ├── stripe
│ │ │ ├── meter.md
│ │ │ ├── price.md
│ │ │ ├── product.md
│ │ │ └── webhook.md
│ │ ├── upstash
│ │ │ └── redis.md
│ │ └── vercel
│ │ │ ├── project-domain.md
│ │ │ └── project.md
│ └── what-is-alchemy.md
├── index.md
├── package.json
└── public
│ ├── alchemist.webp
│ ├── alchemy-flower.png
│ ├── alchemy-og.png
│ ├── potion-with-border.png
│ └── potion.png
├── alchemy
├── package.json
├── src
│ ├── ai
│ │ ├── ark.ts
│ │ ├── astro-file.ts
│ │ ├── client.ts
│ │ ├── css-file.ts
│ │ ├── data.ts
│ │ ├── document.ts
│ │ ├── html-file.ts
│ │ ├── index.ts
│ │ ├── json-file.ts
│ │ ├── typescript-file.ts
│ │ ├── vue-file.ts
│ │ └── yaml-file.ts
│ ├── alchemy.ts
│ ├── apply.ts
│ ├── aws
│ │ ├── account-id.ts
│ │ ├── bucket.ts
│ │ ├── control
│ │ │ ├── client.ts
│ │ │ ├── error.ts
│ │ │ ├── index.ts
│ │ │ ├── properties.ts
│ │ │ ├── proxy.ts
│ │ │ ├── resource.ts
│ │ │ └── types.ts
│ │ ├── credentials.ts
│ │ ├── function.ts
│ │ ├── index.ts
│ │ ├── oidc
│ │ │ ├── github-oidc-provider.ts
│ │ │ ├── index.ts
│ │ │ └── oidc-provider.ts
│ │ ├── policy-attachment.ts
│ │ ├── policy.ts
│ │ ├── queue.ts
│ │ ├── retry.ts
│ │ ├── role.ts
│ │ ├── ses.ts
│ │ ├── ssm-parameter.ts
│ │ └── table.ts
│ ├── cloudflare
│ │ ├── account-api-token.ts
│ │ ├── account-id.ts
│ │ ├── ai-gateway.ts
│ │ ├── ai.ts
│ │ ├── analytics-engine.ts
│ │ ├── api-error.ts
│ │ ├── api.ts
│ │ ├── asset-manifest.ts
│ │ ├── assets.ts
│ │ ├── auth.ts
│ │ ├── bindings.ts
│ │ ├── bound.ts
│ │ ├── browser-rendering.ts
│ │ ├── bucket.ts
│ │ ├── bundle
│ │ │ ├── alias-plugin.ts
│ │ │ ├── build-failures.ts
│ │ │ ├── bundle-worker.ts
│ │ │ ├── external.ts
│ │ │ ├── local-dev-cloudflare-shim.ts
│ │ │ ├── nodejs-compat-mode.ts
│ │ │ ├── nodejs-compat.ts
│ │ │ └── wasm-plugin.ts
│ │ ├── custom-domain.ts
│ │ ├── d1-clone.ts
│ │ ├── d1-database.ts
│ │ ├── d1-export.ts
│ │ ├── d1-import.ts
│ │ ├── d1-migrations.ts
│ │ ├── dns-records.ts
│ │ ├── durable-object-namespace.ts
│ │ ├── event-source.ts
│ │ ├── hyperdrive.ts
│ │ ├── index.ts
│ │ ├── kv-namespace.ts
│ │ ├── nuxt.ts
│ │ ├── permission-groups.ts
│ │ ├── pipeline.ts
│ │ ├── queue-consumer.ts
│ │ ├── queue.ts
│ │ ├── r2-rest-state-store.ts
│ │ ├── react-router.ts
│ │ ├── redwood.ts
│ │ ├── response.ts
│ │ ├── route.ts
│ │ ├── tanstack-start.ts
│ │ ├── types.ts
│ │ ├── user.ts
│ │ ├── vectorize-index.ts
│ │ ├── vectorize-metadata-index.ts
│ │ ├── version-metadata.ts
│ │ ├── vite.ts
│ │ ├── website.ts
│ │ ├── worker-assets.ts
│ │ ├── worker-metadata.ts
│ │ ├── worker-migration.ts
│ │ ├── worker-stub.ts
│ │ ├── worker.ts
│ │ ├── workflow.ts
│ │ ├── wrangler.json.ts
│ │ ├── zone-settings.ts
│ │ └── zone.ts
│ ├── context.ts
│ ├── destroy.ts
│ ├── dns
│ │ ├── godaddy.ts
│ │ ├── import-dns.ts
│ │ ├── index.ts
│ │ └── record.ts
│ ├── encrypt.ts
│ ├── env.ts
│ ├── esbuild
│ │ ├── bundle.ts
│ │ └── index.ts
│ ├── fs
│ │ ├── copy-file.ts
│ │ ├── file-collection.ts
│ │ ├── file-ref.ts
│ │ ├── file-system-state-store.ts
│ │ ├── file.ts
│ │ ├── folder.ts
│ │ ├── index.ts
│ │ ├── static-astro-file.ts
│ │ ├── static-css-file.ts
│ │ ├── static-html-file.ts
│ │ ├── static-json-file.ts
│ │ ├── static-text-file.ts
│ │ ├── static-typescript-file.ts
│ │ ├── static-vue-file.ts
│ │ └── static-yaml-file.ts
│ ├── github
│ │ ├── client.ts
│ │ ├── index.ts
│ │ ├── repository-environment.ts
│ │ └── secret.ts
│ ├── index.ts
│ ├── internal
│ │ └── docs
│ │ │ └── providers.ts
│ ├── neon
│ │ ├── api-error.ts
│ │ ├── api.ts
│ │ ├── index.ts
│ │ └── project.ts
│ ├── os
│ │ ├── exec.ts
│ │ └── index.ts
│ ├── resource.ts
│ ├── runtime
│ │ ├── bind.ts
│ │ ├── global.ts
│ │ ├── plugin.ts
│ │ ├── shims.js
│ │ └── state.ts
│ ├── scope.ts
│ ├── secret.ts
│ ├── sentry
│ │ ├── api.ts
│ │ ├── client-key.ts
│ │ ├── index.ts
│ │ ├── project.ts
│ │ └── team.ts
│ ├── serde.ts
│ ├── state.ts
│ ├── stripe
│ │ ├── index.ts
│ │ ├── meter.ts
│ │ ├── price.ts
│ │ ├── product.ts
│ │ └── webhook.ts
│ ├── test
│ │ ├── bun.ts
│ │ ├── prune.ts
│ │ └── vitest.ts
│ ├── type.ts
│ ├── upstash
│ │ ├── api.ts
│ │ ├── error.ts
│ │ ├── index.ts
│ │ └── redis.ts
│ ├── util
│ │ ├── assert-never.ts
│ │ ├── content-type.ts
│ │ ├── dedent.ts
│ │ ├── ignore.ts
│ │ ├── retry.ts
│ │ ├── rm.ts
│ │ ├── safe-fetch.ts
│ │ ├── sha256.ts
│ │ ├── sleep.ts
│ │ └── slugify.ts
│ ├── vercel
│ │ ├── api.ts
│ │ ├── index.ts
│ │ ├── project-domain.ts
│ │ └── project.ts
│ └── web
│ │ ├── astro.ts
│ │ ├── shadcn-component.ts
│ │ ├── shadcn.ts
│ │ ├── tailwind.ts
│ │ ├── vite.ts
│ │ └── vitepress
│ │ ├── config.ts
│ │ ├── custom-theme.ts
│ │ ├── dependencies.ts
│ │ ├── home-page.ts
│ │ ├── index.ts
│ │ ├── process-front-matter-files.ts
│ │ └── vitepress.ts
├── test
│ ├── alchemy.test.ts
│ ├── aws
│ │ ├── control
│ │ │ ├── proxy.test.ts
│ │ │ ├── resource.test.ts
│ │ │ └── test-utils.ts
│ │ ├── function.test.ts
│ │ ├── queue.test.ts
│ │ ├── role.test.ts
│ │ ├── ses.test.ts
│ │ ├── ssm-parameter.test.ts
│ │ └── table.test.ts
│ ├── cloudflare
│ │ ├── account-api-token.test.ts
│ │ ├── account-id.test.ts
│ │ ├── ai-gateway.test.ts
│ │ ├── ai.test.ts
│ │ ├── browser-handler.ts
│ │ ├── browser-rendering.test.ts
│ │ ├── bucket.test.ts
│ │ ├── bundle-handler-als.ts
│ │ ├── bundle-handler.ts
│ │ ├── bundle.test.ts
│ │ ├── d1-clone.test.ts
│ │ ├── d1-database.test.ts
│ │ ├── dns-records.test.ts
│ │ ├── durable-object.test.ts
│ │ ├── fetch-utils.ts
│ │ ├── hyperdrive.test.ts
│ │ ├── kv-namespace.test.ts
│ │ ├── migrations
│ │ │ └── 001_create_table.sql
│ │ ├── nobundle
│ │ │ ├── dir
│ │ │ │ └── bar.js
│ │ │ ├── foo.js
│ │ │ └── index.js
│ │ ├── permission-groups.test.ts
│ │ ├── pipeline.test.ts
│ │ ├── queue-consumer.test.ts
│ │ ├── queue.test.ts
│ │ ├── r2-rest-state-store.test.ts
│ │ ├── route.test.ts
│ │ ├── unenv-handler.ts
│ │ ├── unenv.test.ts
│ │ ├── vectorize-index.test.ts
│ │ ├── vectorize-metadata-index.test.ts
│ │ ├── version-metadata-handler.ts
│ │ ├── version-metadata.test.ts
│ │ ├── worker.test.ts
│ │ ├── workflow.test.ts
│ │ ├── wrangler-json.test.ts
│ │ └── zone.test.ts
│ ├── esbuild.test.ts
│ ├── fs
│ │ └── copy-file.test.ts
│ ├── github
│ │ ├── repository-environment.test.ts
│ │ └── secret.test.ts
│ ├── handler.ts
│ ├── neon
│ │ └── project.test.ts
│ ├── os
│ │ └── exec.test.ts
│ ├── run.ts
│ ├── runtime
│ │ ├── app.js
│ │ └── app.ts
│ ├── scope.test.ts
│ ├── sentry
│ │ ├── client-key.test.ts
│ │ ├── project.test.ts
│ │ └── team.test.ts
│ ├── serde.test.ts
│ ├── smoke.test.ts
│ ├── stripe
│ │ ├── meter.test.ts
│ │ ├── price.test.ts
│ │ ├── product.test.ts
│ │ └── webhook-endpoint.test.ts
│ ├── upstash
│ │ └── redis.test.ts
│ ├── util.ts
│ ├── util
│ │ └── dedent.test.ts
│ └── vercel
│ │ ├── project-domain.test.ts
│ │ └── project.test.ts
├── tsconfig.json
└── tsconfig.test.json
├── biome.json
├── bun.lock
├── examples
├── aws-app
│ ├── alchemy.run.ts
│ ├── package.json
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── cloudflare-nuxt-pipeline
│ ├── .gitignore
│ ├── README.md
│ ├── alchemy.run.ts
│ ├── app.vue
│ ├── env.d.ts
│ ├── index.ts
│ ├── nuxt.config.ts
│ ├── package.json
│ ├── pages
│ │ └── index.vue
│ ├── public
│ │ ├── favicon.ico
│ │ └── robots.txt
│ ├── server
│ │ ├── api
│ │ │ └── pipeline.post.ts
│ │ └── tsconfig.json
│ └── tsconfig.json
├── cloudflare-react-router
│ ├── .gitignore
│ ├── README.md
│ ├── alchemy.run.ts
│ ├── app
│ │ ├── app.css
│ │ ├── entry.server.tsx
│ │ ├── root.tsx
│ │ ├── routes.ts
│ │ ├── routes
│ │ │ └── home.tsx
│ │ └── welcome
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo-light.svg
│ │ │ └── welcome.tsx
│ ├── bun.lock
│ ├── package.json
│ ├── public
│ │ └── favicon.ico
│ ├── react-router.config.ts
│ ├── tsconfig.cloudflare.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ ├── vite.config.ts
│ └── workers
│ │ ├── app.ts
│ │ └── env.ts
├── cloudflare-redwood
│ ├── .env.example
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── alchemy.run.ts
│ ├── drizzle.config.ts
│ ├── drizzle
│ │ ├── 0000_lame_kitty_pryde.sql
│ │ └── meta
│ │ │ ├── 0000_snapshot.json
│ │ │ └── _journal.json
│ ├── package.json
│ ├── public
│ │ └── images
│ │ │ ├── cloudflare-account-id.png
│ │ │ ├── cloudflare-copy-token.png
│ │ │ ├── cloudflare-custom-tokens.png
│ │ │ ├── cloudflare-d1.png
│ │ │ ├── cloudflare-new-token.png
│ │ │ ├── cloudflare-token-summary.png
│ │ │ ├── cloudflare-user-api-tokens.png
│ │ │ └── new-db.png
│ ├── src
│ │ ├── app
│ │ │ ├── Document.tsx
│ │ │ ├── headers.ts
│ │ │ ├── pages
│ │ │ │ └── Home.tsx
│ │ │ └── shared
│ │ │ │ └── links.ts
│ │ ├── client.tsx
│ │ ├── db
│ │ │ ├── schema.ts
│ │ │ └── seed.ts
│ │ └── worker.tsx
│ ├── tsconfig.json
│ ├── types
│ │ ├── env.d.ts
│ │ ├── rw.d.ts
│ │ └── vite.d.ts
│ ├── vite.config.mts
│ └── worker-configuration.d.ts
├── cloudflare-tanstack-start
│ ├── .gitignore
│ ├── .prettierignore
│ ├── .vscode
│ │ └── settings.json
│ ├── README.md
│ ├── alchemy.run.ts
│ ├── app.config.ts
│ ├── bun.lock
│ ├── package.json
│ ├── postcss.config.mjs
│ ├── public
│ │ ├── android-chrome-192x192.png
│ │ ├── android-chrome-512x512.png
│ │ ├── apple-touch-icon.png
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── favicon.ico
│ │ ├── favicon.png
│ │ └── site.webmanifest
│ ├── src
│ │ ├── api.ts
│ │ ├── client.tsx
│ │ ├── components
│ │ │ ├── DefaultCatchBoundary.tsx
│ │ │ ├── NotFound.tsx
│ │ │ ├── PostError.tsx
│ │ │ └── UserError.tsx
│ │ ├── env.d.ts
│ │ ├── global-middleware.ts
│ │ ├── routeTree.gen.ts
│ │ ├── router.tsx
│ │ ├── routes
│ │ │ ├── __root.tsx
│ │ │ ├── _pathlessLayout.tsx
│ │ │ ├── _pathlessLayout
│ │ │ │ ├── _nested-layout.tsx
│ │ │ │ └── _nested-layout
│ │ │ │ │ ├── route-a.tsx
│ │ │ │ │ └── route-b.tsx
│ │ │ ├── api
│ │ │ │ ├── users.$id.ts
│ │ │ │ └── users.ts
│ │ │ ├── deferred.tsx
│ │ │ ├── index.tsx
│ │ │ ├── posts.$postId.tsx
│ │ │ ├── posts.index.tsx
│ │ │ ├── posts.route.tsx
│ │ │ ├── posts_.$postId.deep.tsx
│ │ │ ├── redirect.tsx
│ │ │ ├── users.$userId.tsx
│ │ │ ├── users.index.tsx
│ │ │ └── users.route.tsx
│ │ ├── ssr.tsx
│ │ ├── styles
│ │ │ └── app.css
│ │ └── utils
│ │ │ ├── loggingMiddleware.tsx
│ │ │ ├── posts.tsx
│ │ │ ├── seo.ts
│ │ │ └── users.tsx
│ ├── tailwind.config.mjs
│ └── tsconfig.json
├── cloudflare-vite
│ ├── .gitignore
│ ├── alchemy.run.ts
│ ├── index.html
│ ├── package.json
│ ├── public
│ │ └── vite.svg
│ ├── src
│ │ ├── App.css
│ │ ├── App.tsx
│ │ ├── assets
│ │ │ └── react.svg
│ │ ├── auth
│ │ │ ├── issuer.ts
│ │ │ └── subjects.ts
│ │ ├── env.d.ts
│ │ ├── index.css
│ │ ├── index.ts
│ │ ├── main.tsx
│ │ └── vite-env.d.ts
│ ├── tsconfig.json
│ └── vite.config.ts
├── cloudflare-worker-bootstrap
│ ├── index.ts
│ ├── package.json
│ └── tsconfig.json
└── cloudflare-worker
│ ├── .gitignore
│ ├── alchemy.run.ts
│ ├── package.json
│ ├── src
│ ├── do.ts
│ ├── env.ts
│ ├── rpc.ts
│ ├── worker.ts
│ └── workflow.ts
│ └── tsconfig.json
├── package.json
├── public
├── alchemist.png
└── alchemist.webp
├── scripts
├── bump.ts
├── generate-aws-control-types.ts
├── generate-aws-control.ts
└── shell.ts
├── stacks
├── docs.run.ts
├── env.ts
├── repo.run.ts
└── website.run.ts
├── tsconfig.base.json
├── tsconfig.json
├── tsconfig.stacks.json
├── vitest.config.ts
└── vitest.setup.ts
/.cursor/rules/cloudflare.mdc:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | globs: alchemy/src/cloudflare/*
4 | alwaysApply: false
5 | ---
6 | When adding a new resource that can be bound to a worker, make sure to update:
7 |
8 | 1. bindings.ts - add the binding type to the union
9 | 2. bound.ts - map the Alchemy resource to the Cloudflare runtime binding type
10 | 3. worker.ts - map the binding to the cloduflare metadata api
11 | 4. {resource}.ts - add a new file for the resource alchemy/src/cloudflare/{resource}.ts
12 | 5. {resource}.test.ts - add a new file for the resource alchemy/test/cloudflare/{resource}.test.ts
--------------------------------------------------------------------------------
/.github/workflows/pkg-pr.yml:
--------------------------------------------------------------------------------
1 | name: Publish Preview Package
2 | on:
3 | push:
4 | branches: [main]
5 | pull_request:
6 |
7 | jobs:
8 | pkg-pr:
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - name: Checkout code
13 | uses: actions/checkout@v4
14 |
15 | - name: Setup Bun
16 | uses: oven-sh/setup-bun@v1
17 | with:
18 | bun-version: latest
19 |
20 | - name: Install dependencies
21 | run: bun install
22 |
23 | - name: Build
24 | run: bun run --filter alchemy build
25 |
26 | - run: bunx pkg-pr-new publish ./alchemy
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | dist/
3 | lib/
4 | .out/
5 | .test/
6 | .env
7 | *.tsbuildinfo
8 |
9 | .alchemy/
10 | # !.alchemy/github:alchemy/
11 | # examples/*/.alchemy
12 |
13 | .repomix-output.txt
14 | alchemy-web/public/og-images/
15 | wrangler.jsonc
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/.gitmodules
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.codeLens": false,
3 | "editor.formatOnSave": true,
4 | "editor.formatOnSaveMode": "file",
5 | "editor.codeActionsOnSave": {
6 | "source.fixAll.ruff": "always",
7 | "source.fixAll": "explicit",
8 | "source.organizeImports": "explicit",
9 | "source.organizeImports.biome": "explicit"
10 | },
11 | "rewrap.autoWrap.enabled": true,
12 | "[rust]": {
13 | "editor.insertSpaces": true,
14 | "editor.tabSize": 4
15 | },
16 | "[toml]": {
17 | "editor.defaultFormatter": "tamasfe.even-better-toml"
18 | },
19 | "[json]": {
20 | "editor.indentSize": 2,
21 | "editor.insertSpaces": true,
22 | "editor.defaultFormatter": "biomejs.biome"
23 | },
24 | "[markdown]": {
25 | "editor.formatOnSave": true
26 | },
27 | "editor.inlayHints.enabled": "offUnlessPressed",
28 | "files.exclude": {
29 | "**/__pycache__": true,
30 | "**/*.pyc": true
31 | },
32 | "vitest.disableWorkspaceWarning": true,
33 | "[typescript]": {
34 | "editor.indentSize": 2,
35 | "editor.defaultFormatter": "biomejs.biome"
36 | },
37 | "[javascript]": {
38 | "editor.indentSize": 2,
39 | "editor.defaultFormatter": "biomejs.biome"
40 | },
41 | "[tsx]": {
42 | "editor.indentSize": 2,
43 | "editor.defaultFormatter": "biomejs.biome"
44 | },
45 | "[jsx]": {
46 | "editor.indentSize": 2,
47 | "editor.defaultFormatter": "biomejs.biome"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/alchemy-web/.gitignore:
--------------------------------------------------------------------------------
1 | .vitepress/cache
2 | .vitepress/.temp
3 |
--------------------------------------------------------------------------------
/alchemy-web/.vitepress/theme/fonts/Inter.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/alchemy-web/.vitepress/theme/fonts/Inter.ttf
--------------------------------------------------------------------------------
/alchemy-web/blogs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Blog
4 | ---
5 |
6 |
28 |
29 | # Blog
30 |
31 |
39 |
40 |
41 |
No blog posts found. Make sure your blog posts have a date in the frontmatter.
42 |
43 |
--------------------------------------------------------------------------------
/alchemy-web/docs/concepts/adoption.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 10
3 | title: Resource Adoption
4 | description: Learn how to adopt existing infrastructure with Alchemy resources instead of failing when resources already exist.
5 | ---
6 |
7 | # Resource Adoption
8 |
9 | When creating a resource, Alchemy will fail if a resource with the same name already exists. Resource adoption allows you to opt in to using the pre-existing resource instead.
10 |
11 | ## Basic Usage
12 |
13 | ```typescript
14 | // Without adoption - fails if bucket already exists
15 | const bucket = await R2Bucket("my-bucket", {
16 | name: "existing-bucket"
17 | });
18 |
19 | // With adoption - uses existing bucket if it exists
20 | const bucket = await R2Bucket("my-bucket", {
21 | name: "existing-bucket",
22 | adopt: true
23 | });
24 | ```
25 |
26 | ## How It Works
27 |
28 | During the **create phase**, if a resource already exists:
29 |
30 | - **Without adoption** (default): Throws an "already exists" error
31 | - **With adoption**: Finds and adopts the existing resource
32 |
33 | ```typescript
34 | // Implementation pattern
35 | if (this.phase === "create") {
36 | try {
37 | // Try to create the resource
38 | return await createResource(props);
39 | } catch (err) {
40 | if (err.status === 409 && props.adopt) {
41 | // Adopt existing resource instead
42 | return await findExistingResource(props);
43 | }
44 | throw err;
45 | }
46 | }
47 | ```
48 |
49 | ## Related Concepts
50 |
51 | - **[Resource](./resource.md)** - Understanding Alchemy's resource model
52 | - **[State](./state.md)** - How Alchemy tracks resource state
53 |
--------------------------------------------------------------------------------
/alchemy-web/docs/guides/custom-resources.md:
--------------------------------------------------------------------------------
1 | ---
2 | order: 6
3 | title: Custom Resource
4 | description: Learn how to build your own custom infrastructure resources for Alchemy using AI-assistance. Extend Alchemy to support any cloud service or API.
5 | ---
6 |
7 | # Custom Resources
8 |
9 | In Alchemy, a Resource is "just a function". This makes it super easy to generate resources for your use-cases using Agentic IDEs like Cursor, Claude Code, Windsurf, etc.
10 |
11 | ## Cursorrules
12 |
13 | To start generating resources, copy Alchemy's [.cursorrules](https://github.com/sam-goodwin/alchemy/blob/main/.cursorrules) into your repo
14 |
15 | > [!NOTE]
16 | > All of Alchemy's "built-in" resouces are generated this way, so it is tried and tested.
17 |
18 | ## Simple Prompt Example
19 |
20 | As an example, let's show how easy it is to generate a resource for Neon's famous serverless `Database` Resource.
21 |
22 | It usually doesn't take much to get 90% of the way there - a simple prompt with a link to the API docs is a good start:
23 |
24 | > Create a Resource for managing a Neon Database
25 | > See: https://api-docs.neon.tech/reference/createprojectbranchdatabase
26 |
27 | This will generate the Resource implementation and tests.
28 |
29 | ## Resource Implememtation
30 |
31 | See the [Resource Documentation](../concepts/resource.md) for a comprehensive overview of a Resource.
32 |
33 | ## Test Suite Implementation
34 |
35 | See the [Testing Documentation](../concepts/testing.md) for a comprehensive overview of how to test your Resources.
--------------------------------------------------------------------------------
/alchemy-web/docs/index.md:
--------------------------------------------------------------------------------
1 | # Alchemy
2 |
3 | Alchemy is an embeddable, zero-dependency Infrastructure-as-Code library in pure TypeScript that runs anywhere JavaScript runs.
4 |
5 | - [What is Alchemy?](./what-is-alchemy.md)
6 | - [Getting Started](./getting-started.md)
7 |
8 |
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/ai/html-file.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing HTML Files with AI in Alchemy
3 | description: Learn how to use Alchemy's AI provider to create, update, and manage HTML (.html) files within your projects.
4 | ---
5 |
6 | # HTMLFile
7 |
8 | The HTMLFile resource lets you generate HTML files using AI models like [OpenAI GPT-4](https://platform.openai.com/docs/models/gpt-4) or [Anthropic Claude](https://www.anthropic.com/claude).
9 |
10 | ## Minimal Example
11 |
12 | Creates a basic HTML file with AI-generated content.
13 |
14 | ```ts
15 | import { HTMLFile } from "alchemy/ai";
16 |
17 | const page = await HTMLFile("landing", {
18 | path: "./public/index.html",
19 | prompt: "Generate a simple landing page with a hero section, features list, and contact form"
20 | });
21 | ```
22 |
23 | ## Generate with Context
24 |
25 | Uses file context to generate HTML that matches existing code.
26 |
27 | ```ts
28 | import { HTMLFile } from "alchemy/ai";
29 |
30 | const component = await HTMLFile("card", {
31 | path: "./components/card.html",
32 | prompt: await alchemy`
33 | Create an HTML card component that matches the style of:
34 | ${alchemy.file("components/button.html")}
35 | `
36 | });
37 | ```
38 |
39 | ## Custom Model Configuration
40 |
41 | Specifies a custom model and temperature for more controlled generation.
42 |
43 | ```ts
44 | import { HTMLFile } from "alchemy/ai";
45 |
46 | const form = await HTMLFile("contact-form", {
47 | path: "./components/form.html",
48 | prompt: "Generate an accessible contact form with validation",
49 | model: {
50 | id: "claude-3-opus-20240229",
51 | provider: "anthropic"
52 | },
53 | temperature: 0.2
54 | });
55 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/aws-control/application-signals/discovery.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing AWS ApplicationSignals Discoverys with Alchemy
3 | description: Learn how to create, update, and manage AWS ApplicationSignals Discoverys using Alchemy Cloud Control.
4 | ---
5 |
6 | # Discovery
7 |
8 | The Discovery resource lets you manage [AWS ApplicationSignals Discoverys](https://docs.aws.amazon.com/applicationsignals/latest/userguide/) for tracking application performance and signals.
9 |
10 | ## Minimal Example
11 |
12 | Create a basic Discovery resource with minimal properties.
13 |
14 | ```ts
15 | import AWS from "alchemy/aws/control";
16 |
17 | const basicDiscovery = await AWS.ApplicationSignals.Discovery("basic-discovery", {
18 | adopt: false // Default is false; it will fail if the resource already exists
19 | });
20 | ```
21 |
22 | ## Advanced Configuration
23 |
24 | Configure a Discovery resource with the option to adopt an existing resource.
25 |
26 | ```ts
27 | const advancedDiscovery = await AWS.ApplicationSignals.Discovery("advanced-discovery", {
28 | adopt: true // This will adopt the existing resource instead of failing
29 | });
30 | ```
31 |
32 | ## Monitoring Discovery Details
33 |
34 | Create a Discovery resource and monitor its properties such as ARN and timestamps.
35 |
36 | ```ts
37 | const monitoredDiscovery = await AWS.ApplicationSignals.Discovery("monitored-discovery", {
38 | adopt: false
39 | });
40 |
41 | // Log details about the Discovery resource
42 | console.log(`ARN: ${monitoredDiscovery.Arn}`);
43 | console.log(`Creation Time: ${monitoredDiscovery.CreationTime}`);
44 | console.log(`Last Update Time: ${monitoredDiscovery.LastUpdateTime}`);
45 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/aws-control/fraud-detector/label.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing AWS FraudDetector Labels with Alchemy
3 | description: Learn how to create, update, and manage AWS FraudDetector Labels using Alchemy Cloud Control.
4 | ---
5 |
6 | # Label
7 |
8 | The Label resource lets you manage [AWS FraudDetector Labels](https://docs.aws.amazon.com/frauddetector/latest/userguide/) for identifying and categorizing events for fraud detection.
9 |
10 | ## Minimal Example
11 |
12 | Create a basic label with just the required properties and a description.
13 |
14 | ```ts
15 | import AWS from "alchemy/aws/control";
16 |
17 | const fraudLabel = await AWS.FraudDetector.Label("fraud-label", {
18 | name: "SuspiciousTransaction",
19 | description: "Label for transactions flagged as suspicious"
20 | });
21 | ```
22 |
23 | ## Advanced Configuration
24 |
25 | Create a label with tags for better organization and management.
26 |
27 | ```ts
28 | const taggedLabel = await AWS.FraudDetector.Label("tagged-fraud-label", {
29 | name: "HighRiskTransaction",
30 | description: "Label for transactions identified as high risk",
31 | tags: [
32 | { key: "Environment", value: "Production" },
33 | { key: "Team", value: "FraudDetection" }
34 | ]
35 | });
36 | ```
37 |
38 | ## Update Existing Label
39 |
40 | Adopt an existing label instead of failing if it already exists.
41 |
42 | ```ts
43 | const existingLabel = await AWS.FraudDetector.Label("existing-fraud-label", {
44 | name: "ReturningCustomer",
45 | description: "Label for transactions from returning customers",
46 | adopt: true // This will adopt if the label already exists
47 | });
48 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/aws-control/glue/registry.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing AWS Glue Registrys with Alchemy
3 | description: Learn how to create, update, and manage AWS Glue Registrys using Alchemy Cloud Control.
4 | ---
5 |
6 | # Registry
7 |
8 | The Registry resource lets you manage [AWS Glue Registrys](https://docs.aws.amazon.com/glue/latest/userguide/) for organizing and managing schemas in AWS Glue Data Catalog.
9 |
10 | ## Minimal Example
11 |
12 | Create a basic registry with a name and description.
13 |
14 | ```ts
15 | import AWS from "alchemy/aws/control";
16 |
17 | const basicRegistry = await AWS.Glue.Registry("basic-registry", {
18 | name: "MyDataRegistry",
19 | description: "This registry holds schemas for my data assets."
20 | });
21 | ```
22 |
23 | ## Advanced Configuration
24 |
25 | Configure a registry with tags for better management and organization.
26 |
27 | ```ts
28 | const taggedRegistry = await AWS.Glue.Registry("tagged-registry", {
29 | name: "MyTaggedDataRegistry",
30 | description: "This registry holds schemas for my data assets with tags.",
31 | tags: [
32 | { key: "Environment", value: "Production" },
33 | { key: "Department", value: "Finance" }
34 | ]
35 | });
36 | ```
37 |
38 | ## Adoption of Existing Registry
39 |
40 | Adopt an existing registry instead of failing if it already exists.
41 |
42 | ```ts
43 | const adoptRegistry = await AWS.Glue.Registry("adopt-registry", {
44 | name: "ExistingDataRegistry",
45 | description: "This registry will adopt an existing resource.",
46 | adopt: true
47 | });
48 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/aws-control/health-imaging/datastore.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing AWS HealthImaging Datastores with Alchemy
3 | description: Learn how to create, update, and manage AWS HealthImaging Datastores using Alchemy Cloud Control.
4 | ---
5 |
6 | # Datastore
7 |
8 | The Datastore resource lets you manage [AWS HealthImaging Datastores](https://docs.aws.amazon.com/healthimaging/latest/userguide/) for storing and retrieving medical imaging data.
9 |
10 | ## Minimal Example
11 |
12 | Create a basic HealthImaging Datastore with a specified name and an optional KMS key for encryption.
13 |
14 | ```ts
15 | import AWS from "alchemy/aws/control";
16 |
17 | const basicDatastore = await AWS.HealthImaging.Datastore("basic-datastore", {
18 | DatastoreName: "patient-imaging-data",
19 | KmsKeyArn: "arn:aws:kms:us-west-2:123456789012:key/abcd1234-ab12-cd34-ef56-1234567890ab"
20 | });
21 | ```
22 |
23 | ## Advanced Configuration
24 |
25 | Configure a datastore with tags for better resource management and organization.
26 |
27 | ```ts
28 | const taggedDatastore = await AWS.HealthImaging.Datastore("tagged-datastore", {
29 | DatastoreName: "research-study-images",
30 | KmsKeyArn: "arn:aws:kms:us-west-2:123456789012:key/abcd1234-ab12-cd34-ef56-1234567890ab",
31 | Tags: {
32 | Project: "Medical Research",
33 | Environment: "Production"
34 | }
35 | });
36 | ```
37 |
38 | ## Adoption of Existing Resources
39 |
40 | Adopt an existing HealthImaging Datastore instead of failing if the resource already exists.
41 |
42 | ```ts
43 | const adoptExistingDatastore = await AWS.HealthImaging.Datastore("adopt-datastore", {
44 | DatastoreName: "legacy-datastore",
45 | adopt: true
46 | });
47 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/aws-control/ivs/public-key.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing AWS IVS PublicKeys with Alchemy
3 | description: Learn how to create, update, and manage AWS IVS PublicKeys using Alchemy Cloud Control.
4 | ---
5 |
6 | # PublicKey
7 |
8 | The PublicKey resource lets you manage [AWS IVS PublicKeys](https://docs.aws.amazon.com/ivs/latest/userguide/) for securely signing video streams. This resource allows you to create and manage public keys used in the signing process of video streams.
9 |
10 | ## Minimal Example
11 |
12 | Create a basic IVS PublicKey with required properties and one optional property:
13 |
14 | ```ts
15 | import AWS from "alchemy/aws/control";
16 |
17 | const publicKey = await AWS.IVS.PublicKey("myPublicKey", {
18 | PublicKeyMaterial: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC1...",
19 | Name: "MyIVSPublicKey"
20 | });
21 | ```
22 |
23 | ## Advanced Configuration
24 |
25 | Configure an IVS PublicKey with tags for better resource management:
26 |
27 | ```ts
28 | const taggedPublicKey = await AWS.IVS.PublicKey("taggedPublicKey", {
29 | PublicKeyMaterial: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC2...",
30 | Name: "TaggedIVSPublicKey",
31 | Tags: [
32 | { Key: "Environment", Value: "Production" },
33 | { Key: "Project", Value: "LiveStream" }
34 | ]
35 | });
36 | ```
37 |
38 | ## Adoption of Existing Resource
39 |
40 | Adopt an existing IVS PublicKey instead of creating a new one if it already exists:
41 |
42 | ```ts
43 | const existingPublicKey = await AWS.IVS.PublicKey("existingPublicKey", {
44 | PublicKeyMaterial: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3...",
45 | Name: "ExistingIVSPublicKey",
46 | adopt: true
47 | });
48 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/aws-control/macie/session.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing AWS Macie Sessions with Alchemy
3 | description: Learn how to create, update, and manage AWS Macie Sessions using Alchemy Cloud Control.
4 | ---
5 |
6 | # Session
7 |
8 | The Session resource lets you manage [AWS Macie Sessions](https://docs.aws.amazon.com/macie/latest/userguide/) for data security and compliance within your AWS environment. AWS Macie helps you discover and protect sensitive data in your AWS accounts.
9 |
10 | ## Minimal Example
11 |
12 | Create a basic Macie session with default settings.
13 |
14 | ```ts
15 | import AWS from "alchemy/aws/control";
16 |
17 | const macieSession = await AWS.Macie.Session("basicMacieSession", {
18 | Status: "ENABLED",
19 | FindingPublishingFrequency: "FIFTEEN_MINUTES"
20 | });
21 | ```
22 |
23 | ## Advanced Configuration
24 |
25 | Configure a Macie session with an advanced setting for publishing findings.
26 |
27 | ```ts
28 | const advancedMacieSession = await AWS.Macie.Session("advancedMacieSession", {
29 | Status: "ENABLED",
30 | FindingPublishingFrequency: "ONE_HOUR"
31 | });
32 | ```
33 |
34 | ## Session with Adoption
35 |
36 | Create a Macie session and adopt it if it already exists.
37 |
38 | ```ts
39 | const adoptMacieSession = await AWS.Macie.Session("adoptMacieSession", {
40 | Status: "DISABLED",
41 | FindingPublishingFrequency: "SIX_HOURS",
42 | adopt: true
43 | });
44 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/aws-control/route53profiles/profile.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing AWS Route53Profiles Profiles with Alchemy
3 | description: Learn how to create, update, and manage AWS Route53Profiles Profiles using Alchemy Cloud Control.
4 | ---
5 |
6 | # Profile
7 |
8 | The Profile resource lets you manage [AWS Route53Profiles Profiles](https://docs.aws.amazon.com/route53profiles/latest/userguide/) for configuring and managing DNS settings in AWS Route 53.
9 |
10 | ## Minimal Example
11 |
12 | Create a basic profile with required properties and one optional tag.
13 |
14 | ```ts
15 | import AWS from "alchemy/aws/control";
16 |
17 | const profile = await AWS.Route53Profiles.Profile("basicProfile", {
18 | name: "BasicProfile",
19 | tags: [
20 | {
21 | key: "Environment",
22 | value: "Development"
23 | }
24 | ]
25 | });
26 | ```
27 |
28 | ## Advanced Configuration
29 |
30 | Configure a profile with additional properties such as the adoption of existing resources.
31 |
32 | ```ts
33 | const advancedProfile = await AWS.Route53Profiles.Profile("advancedProfile", {
34 | name: "AdvancedProfile",
35 | adopt: true,
36 | tags: [
37 | {
38 | key: "Project",
39 | value: "Route53Migration"
40 | },
41 | {
42 | key: "Owner",
43 | value: "DevTeam"
44 | }
45 | ]
46 | });
47 | ```
48 |
49 | ## Resource Adoption
50 |
51 | Create a profile while adopting an existing resource if it already exists.
52 |
53 | ```ts
54 | const adoptedProfile = await AWS.Route53Profiles.Profile("adoptedProfile", {
55 | name: "AdoptedProfile",
56 | adopt: true
57 | });
58 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/aws-control/security-hub/hub.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing AWS SecurityHub Hubs with Alchemy
3 | description: Learn how to create, update, and manage AWS SecurityHub Hubs using Alchemy Cloud Control.
4 | ---
5 |
6 | # Hub
7 |
8 | The Hub resource lets you manage [AWS SecurityHub Hubs](https://docs.aws.amazon.com/securityhub/latest/userguide/) for centralizing security findings and compliance checks across your AWS accounts.
9 |
10 | ## Minimal Example
11 |
12 | Create a basic SecurityHub Hub with default settings and enable default standards.
13 |
14 | ```ts
15 | import AWS from "alchemy/aws/control";
16 |
17 | const securityHubHub = await AWS.SecurityHub.Hub("defaultSecurityHub", {
18 | EnableDefaultStandards: true,
19 | ControlFindingGenerator: "AWS"
20 | });
21 | ```
22 |
23 | ## Advanced Configuration
24 |
25 | Configure a SecurityHub Hub with automatic control enabling and specific tags for management.
26 |
27 | ```ts
28 | const advancedSecurityHubHub = await AWS.SecurityHub.Hub("advancedSecurityHub", {
29 | EnableDefaultStandards: true,
30 | AutoEnableControls: true,
31 | Tags: {
32 | Environment: "Production",
33 | Department: "Security"
34 | }
35 | });
36 | ```
37 |
38 | ## Adoption of Existing Resources
39 |
40 | If you need to adopt an existing SecurityHub Hub without failing if it already exists, you can set the `adopt` property to true.
41 |
42 | ```ts
43 | const adoptedSecurityHubHub = await AWS.SecurityHub.Hub("adoptedSecurityHub", {
44 | EnableDefaultStandards: true,
45 | adopt: true
46 | });
47 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/aws-control/service-catalog/portfolio-share.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing AWS ServiceCatalog PortfolioShares with Alchemy
3 | description: Learn how to create, update, and manage AWS ServiceCatalog PortfolioShares using Alchemy Cloud Control.
4 | ---
5 |
6 | # PortfolioShare
7 |
8 | The PortfolioShare resource lets you manage [AWS ServiceCatalog PortfolioShares](https://docs.aws.amazon.com/servicecatalog/latest/userguide/) for sharing portfolios across AWS accounts.
9 |
10 | ## Minimal Example
11 |
12 | Create a basic PortfolioShare with required properties and one optional property.
13 |
14 | ```ts
15 | import AWS from "alchemy/aws/control";
16 |
17 | const portfolioShare = await AWS.ServiceCatalog.PortfolioShare("basicPortfolioShare", {
18 | AccountId: "123456789012",
19 | PortfolioId: "portfolio-abc-123",
20 | AcceptLanguage: "en"
21 | });
22 | ```
23 |
24 | ## Advanced Configuration
25 |
26 | Configure a PortfolioShare to enable sharing of tag options.
27 |
28 | ```ts
29 | const advancedPortfolioShare = await AWS.ServiceCatalog.PortfolioShare("advancedPortfolioShare", {
30 | AccountId: "123456789013",
31 | PortfolioId: "portfolio-def-456",
32 | ShareTagOptions: true
33 | });
34 | ```
35 |
36 | ## Adopting Existing Resources
37 |
38 | Configure a PortfolioShare to adopt existing resources instead of failing if the resource already exists.
39 |
40 | ```ts
41 | const adoptPortfolioShare = await AWS.ServiceCatalog.PortfolioShare("adoptExistingPortfolioShare", {
42 | AccountId: "123456789014",
43 | PortfolioId: "portfolio-ghi-789",
44 | adopt: true
45 | });
46 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/aws/bucket.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing AWS S3 Buckets with Alchemy
3 | description: Learn how to create, configure, and manage AWS S3 Buckets using Alchemy for object storage in your cloud applications.
4 | ---
5 |
6 | # Bucket
7 |
8 | The Bucket resource lets you create and manage [Amazon S3 buckets](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html) for object storage.
9 |
10 | ## Minimal Example
11 |
12 | Create a basic S3 bucket with default settings:
13 |
14 | ```ts
15 | import { Bucket } from "alchemy/aws";
16 |
17 | const bucket = await Bucket("storage", {
18 | bucketName: "my-app-storage",
19 | tags: {
20 | Environment: "production"
21 | }
22 | });
23 | ```
24 |
25 | ## Bucket with Versioning
26 |
27 | Create a bucket with versioning enabled for change tracking:
28 |
29 | ```ts
30 | import { Bucket } from "alchemy/aws";
31 |
32 | const versionedBucket = await Bucket("document-archive", {
33 | bucketName: "document-archive",
34 | tags: {
35 | Environment: "production",
36 | Purpose: "document-storage",
37 | Versioning: "enabled"
38 | }
39 | });
40 | ```
41 |
42 | ## Development Bucket
43 |
44 | Create a temporary bucket for development/testing:
45 |
46 | ```ts
47 | import { Bucket } from "alchemy/aws";
48 |
49 | const devBucket = await Bucket("dev-testing", {
50 | bucketName: "dev-testing",
51 | tags: {
52 | Environment: "development",
53 | Temporary: "true"
54 | }
55 | });
56 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/aws/policy-attachment.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing AWS IAM Policy Attachments with Alchemy
3 | description: Learn how to attach AWS IAM Policies to Roles, Users, or Groups using Alchemy to manage permissions effectively.
4 | ---
5 |
6 | # PolicyAttachment
7 |
8 | The PolicyAttachment resource lets you attach [AWS IAM policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html) to IAM roles.
9 |
10 | ## Minimal Example
11 |
12 | Attach an AWS managed policy to a role:
13 |
14 | ```ts
15 | import { PolicyAttachment } from "alchemy/aws";
16 |
17 | const adminAccess = await PolicyAttachment("admin-policy", {
18 | policyArn: "arn:aws:iam::aws:policy/AdministratorAccess",
19 | roleName: role.name
20 | });
21 | ```
22 |
23 | ## Attach Custom Policy
24 |
25 | Attach a custom policy created with the Policy resource:
26 |
27 | ```ts
28 | import { PolicyAttachment } from "alchemy/aws";
29 |
30 | const customPolicy = await PolicyAttachment("custom-policy", {
31 | policyArn: policy.arn,
32 | roleName: role.name
33 | });
34 | ```
35 |
36 | ## Multiple Policy Attachments
37 |
38 | Attach multiple policies to a role:
39 |
40 | ```ts
41 | import { PolicyAttachment } from "alchemy/aws";
42 |
43 | const s3Access = await PolicyAttachment("s3-access", {
44 | policyArn: "arn:aws:iam::aws:policy/AmazonS3FullAccess",
45 | roleName: role.name
46 | });
47 |
48 | const sqsAccess = await PolicyAttachment("sqs-access", {
49 | policyArn: "arn:aws:iam::aws:policy/AmazonSQSFullAccess",
50 | roleName: role.name
51 | });
52 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/aws/queue.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing AWS SQS Queues with Alchemy
3 | description: Learn how to create, configure, and manage AWS Simple Queue Service (SQS) queues using Alchemy for message queuing.
4 | ---
5 |
6 | # Queue
7 |
8 | The Queue resource lets you create and manage [Amazon Simple Queue Service (SQS)](https://aws.amazon.com/sqs/) queues for reliable message delivery between distributed application components.
9 |
10 | ## Minimal Example
11 |
12 | Create a standard SQS queue with default settings:
13 |
14 | ```ts
15 | import { Queue } from "alchemy/aws";
16 |
17 | const queue = await Queue("my-queue", {
18 | queueName: "my-queue",
19 | tags: {
20 | Environment: "production"
21 | }
22 | });
23 | ```
24 |
25 | ## FIFO Queue
26 |
27 | Create a FIFO queue with content-based deduplication:
28 |
29 | ```ts
30 | import { Queue } from "alchemy/aws";
31 |
32 | const fifoQueue = await Queue("orders-queue", {
33 | queueName: "orders-queue.fifo",
34 | fifo: true,
35 | contentBasedDeduplication: true,
36 | visibilityTimeout: 30
37 | });
38 | ```
39 |
40 | ## Custom Queue Configuration
41 |
42 | Create a queue with custom message handling settings:
43 |
44 | ```ts
45 | import { Queue } from "alchemy/aws";
46 |
47 | const customQueue = await Queue("large-messages", {
48 | queueName: "large-messages",
49 | messageRetentionPeriod: 345600, // 4 days
50 | maximumMessageSize: 262144, // 256 KB
51 | visibilityTimeout: 60,
52 | delaySeconds: 5,
53 | receiveMessageWaitTimeSeconds: 20
54 | });
55 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/aws/ses.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing AWS SES with Alchemy
3 | description: Learn how to configure AWS Simple Email Service (SES) for sending emails using Alchemy in your applications.
4 | ---
5 |
6 | # SES
7 |
8 | The SES resource lets you create and manage [Amazon Simple Email Service (SES)](https://docs.aws.amazon.com/ses/latest/dg/Welcome.html) configuration sets and email identities.
9 |
10 | ## Minimal Example
11 |
12 | Create a basic configuration set for sending emails:
13 |
14 | ```ts
15 | import { SES } from "alchemy/aws";
16 |
17 | const configSet = await SES("email-config", {
18 | configurationSetName: "my-email-config",
19 | sendingOptions: {
20 | SendingEnabled: true
21 | }
22 | });
23 | ```
24 |
25 | ## Create Domain Identity with DKIM
26 |
27 | Create and verify a domain identity with DKIM signing enabled:
28 |
29 | ```ts
30 | const domainIdentity = await SES("domain-identity", {
31 | emailIdentity: "example.com",
32 | enableDkim: true,
33 | tags: {
34 | Environment: "production"
35 | }
36 | });
37 | ```
38 |
39 | ## Configure Tracking Options
40 |
41 | Set up tracking options for open and click tracking:
42 |
43 | ```ts
44 | const emailConfig = await SES("tracking-config", {
45 | configurationSetName: "tracking-config",
46 | trackingOptions: {
47 | CustomRedirectDomain: "click.example.com"
48 | },
49 | suppressionOptions: {
50 | SuppressedReasons: ["BOUNCE", "COMPLAINT"]
51 | }
52 | });
53 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/aws/table.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing AWS DynamoDB Tables with Alchemy
3 | description: Learn how to create, configure, and manage AWS DynamoDB Tables using Alchemy for NoSQL database solutions.
4 | ---
5 |
6 | # DynamoDB Table
7 |
8 | The Table resource lets you create and manage [Amazon DynamoDB tables](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html) for NoSQL database storage.
9 |
10 | ## Minimal Example
11 |
12 | Create a basic table with just a partition key:
13 |
14 | ```ts
15 | import { Table } from "alchemy/aws";
16 |
17 | const table = await Table("users", {
18 | tableName: "users",
19 | partitionKey: {
20 | name: "userId",
21 | type: "S"
22 | }
23 | });
24 | ```
25 |
26 | ## Table with Sort Key
27 |
28 | Add a sort key to enable range queries and composite keys:
29 |
30 | ```ts
31 | const table = await Table("events", {
32 | tableName: "events",
33 | partitionKey: {
34 | name: "deviceId",
35 | type: "S"
36 | },
37 | sortKey: {
38 | name: "timestamp",
39 | type: "N"
40 | }
41 | });
42 | ```
43 |
44 | ## Provisioned Capacity
45 |
46 | Configure provisioned read/write capacity for predictable workloads:
47 |
48 | ```ts
49 | const table = await Table("orders", {
50 | tableName: "orders",
51 | partitionKey: {
52 | name: "orderId",
53 | type: "S"
54 | },
55 | billingMode: "PROVISIONED",
56 | readCapacity: 100,
57 | writeCapacity: 50,
58 | tags: {
59 | Environment: "production"
60 | }
61 | });
62 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/cloudflare/account-id.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Getting Cloudflare Account ID with Alchemy
3 | description: Learn how to retrieve your Cloudflare Account ID programmatically using Alchemy for use in other resource configurations.
4 | ---
5 |
6 | # AccountId
7 |
8 | The AccountId resource retrieves a Cloudflare [Account ID](https://developers.cloudflare.com/fundamentals/setup/find-account-and-zone-ids/) for use with other Cloudflare resources.
9 |
10 | ## Minimal Example
11 |
12 | Get the account ID from environment variables or API token:
13 |
14 | ```ts
15 | import { AccountId } from "alchemy/cloudflare";
16 |
17 | const accountId = await AccountId("my-account");
18 | ```
19 |
20 | ## With Explicit API Key
21 |
22 | Provide an API key and email directly:
23 |
24 | ```ts
25 | import { AccountId } from "alchemy/cloudflare";
26 |
27 | const accountId = await AccountId("my-account", {
28 | apiKey: alchemy.secret(process.env.CF_API_KEY),
29 | email: "user@example.com"
30 | });
31 | ```
32 |
33 | ## Bind to a Worker
34 |
35 | Use the account ID with a Worker:
36 |
37 | ```ts
38 | import { Worker, AccountId } from "alchemy/cloudflare";
39 |
40 | const accountId = await AccountId("my-account");
41 |
42 | await Worker("my-worker", {
43 | name: "my-worker",
44 | script: "console.log('Hello, world!')",
45 | accountId: accountId
46 | });
47 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/cloudflare/assets.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing Cloudflare Assets (Static) with Alchemy
3 | description: Learn how to deploy and manage static assets on Cloudflare using Alchemy for optimal performance and delivery.
4 | ---
5 |
6 | # Assets
7 |
8 | The Assets resource lets you add [static assets](https://developers.cloudflare.com/workers/configuration/sites/) to your Cloudflare Workers.
9 |
10 | ## Minimal Example
11 |
12 | Create a basic assets bundle from a local directory:
13 |
14 | ```ts
15 | import { Assets } from "alchemy/cloudflare";
16 |
17 | const staticAssets = await Assets("static", {
18 | path: "./src/assets"
19 | });
20 | ```
21 |
22 | ## Bind to a Worker
23 |
24 | Bind the assets to a worker to serve them:
25 |
26 | ```ts
27 | import { Worker, Assets } from "alchemy/cloudflare";
28 |
29 | const staticAssets = await Assets("static", {
30 | path: "./src/assets"
31 | });
32 |
33 | const worker = await Worker("frontend", {
34 | name: "frontend-worker",
35 | entrypoint: "./src/worker.ts",
36 | bindings: {
37 | ASSETS: staticAssets
38 | }
39 | });
40 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/cloudflare/custom-domain.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing Cloudflare Custom Domains with Alchemy
3 | description: Learn how to configure and manage Custom Domains for your Cloudflare services (like Pages, Workers) using Alchemy.
4 | ---
5 |
6 | # CustomDomain
7 |
8 | The CustomDomain resource lets you attach a [custom domain](https://developers.cloudflare.com/workers/configuration/routing/custom-domains/) to a Cloudflare Worker.
9 |
10 | ## Minimal Example
11 |
12 | Bind a domain to a worker:
13 |
14 | ```ts
15 | import { Worker, CustomDomain } from "alchemy/cloudflare";
16 |
17 | const worker = await Worker("api", {
18 | name: "api-worker",
19 | entrypoint: "./src/api.ts"
20 | });
21 |
22 | const domain = await CustomDomain("api-domain", {
23 | name: "api.example.com",
24 | zoneId: "YOUR_ZONE_ID",
25 | workerName: worker.name
26 | });
27 | ```
28 |
29 | ## With Environment
30 |
31 | Bind a domain to a specific worker environment:
32 |
33 | ```ts
34 | import { Worker, CustomDomain } from "alchemy/cloudflare";
35 |
36 | const domain = await CustomDomain("staging-domain", {
37 | name: "staging.example.com",
38 | zoneId: "YOUR_ZONE_ID",
39 | workerName: "my-worker",
40 | environment: "staging"
41 | });
42 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/cloudflare/nuxt.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Deploying Nuxt Applications to Cloudflare with Alchemy
3 | description: Learn how to deploy Nuxt.js applications to Cloudflare Pages/Workers using Alchemy for a seamless experience.
4 | ---
5 |
6 | # Nuxt
7 |
8 | Deploy a [Nuxt](https://nuxt.com) application to Cloudflare Pages with automatically configured defaults.
9 |
10 | ## Minimal Example
11 |
12 | Deploy a basic Nuxt site with default settings.
13 |
14 | ```ts
15 | import { Nuxt } from "alchemy/cloudflare";
16 |
17 | const nuxtSite = await Nuxt("my-nuxt-app");
18 | ```
19 |
20 | ## Custom Bindings
21 |
22 | Add database and other bindings to your Nuxt app.
23 |
24 | ```ts
25 | import { Nuxt, D1Database } from "alchemy/cloudflare";
26 |
27 | const db = await D1Database("my-db", {
28 | name: "my-db"
29 | });
30 |
31 | const nuxtSiteWithDb = await Nuxt("my-nuxt-app-with-db", {
32 | command: "npm run build:cloudflare", // Custom build command
33 | bindings: {
34 | DB: db // Add custom bindings
35 | }
36 | });
37 | ```
38 |
39 | ## Bind to a Worker
40 |
41 | Bind a Nuxt app to a Cloudflare Worker.
42 |
43 | ```ts
44 | import { Worker, Nuxt } from "alchemy/cloudflare";
45 |
46 | const nuxtApp = await Nuxt("my-nuxt-app", {
47 | command: "npm run build"
48 | });
49 |
50 | await Worker("my-worker", {
51 | name: "my-worker",
52 | script: "console.log('Hello, world!')",
53 | bindings: {
54 | NUXT: nuxtApp
55 | }
56 | });
57 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/cloudflare/permission-groups.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing Cloudflare Permission Groups with Alchemy
3 | description: Learn how to retrieve Cloudflare API Permission Groups using Alchemy to help construct API token policies.
4 | ---
5 |
6 | # PermissionGroups
7 |
8 | Lists all permission groups available for a Cloudflare account and returns a typed map of permission names to their IDs. Used when creating API tokens for Cloudflare services like R2.
9 |
10 | ## Minimal Example
11 |
12 | Get all permission groups including those for R2:
13 |
14 | ```ts
15 | import { PermissionGroups } from "alchemy/cloudflare";
16 |
17 | const permissions = await PermissionGroups("cloudflare-permissions");
18 | ```
19 |
20 | ## Create API Token with Permissions
21 |
22 | Use with AccountApiToken to create a token with proper permissions:
23 |
24 | ```ts
25 | import { PermissionGroups, AccountApiToken } from "alchemy/cloudflare";
26 |
27 | const permissions = await PermissionGroups("cloudflare-permissions");
28 |
29 | const token = await AccountApiToken("r2-token", {
30 | name: "R2 Read-Only Token",
31 | policies: [{
32 | effect: "allow",
33 | resources: {
34 | "com.cloudflare.edge.r2.bucket.abc123_default_my-bucket": "*"
35 | },
36 | permissionGroups: [{
37 | id: permissions["Workers R2 Storage Bucket Item Read"].id
38 | }]
39 | }]
40 | });
41 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/cloudflare/vite.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Deploying Vite Applications to Cloudflare with Alchemy
3 | description: Learn how to deploy Vite.js applications to Cloudflare Pages/Workers using Alchemy for fast and efficient builds.
4 | ---
5 |
6 | # Vite
7 |
8 | Deploy a [Vite](https://vitejs.dev/) application to Cloudflare Workers with automatic configuration.
9 |
10 | ## Minimal Example
11 |
12 | Deploy a basic Vite app with default settings.
13 |
14 | ```ts
15 | import { Vite } from "alchemy/cloudflare";
16 |
17 | const app = await Vite("my-vite-app", {
18 | name: "my-vite-app",
19 | command: "bun run build"
20 | });
21 | ```
22 |
23 | ## With Custom Bindings
24 |
25 | Add database and environment bindings to the Vite app.
26 |
27 | ```ts
28 | import { Vite, D1Database } from "alchemy/cloudflare";
29 |
30 | const db = await D1Database("my-db", {
31 | name: "my-db"
32 | });
33 |
34 | const app = await Vite("my-vite-app", {
35 | name: "my-vite-app",
36 | bindings: {
37 | DB: db,
38 | API_KEY: alchemy.secret(process.env.API_KEY)
39 | }
40 | });
41 | ```
42 |
43 | ## With Custom Build Configuration
44 |
45 | Customize the build command and output paths.
46 |
47 | ```ts
48 | import { Vite } from "alchemy/cloudflare";
49 |
50 | const app = await Vite("my-vite-app", {
51 | name: "my-vite-app",
52 | command: "bun run test && bun run build:production",
53 | main: "./dist/worker.js",
54 | assets: "./dist/client"
55 | });
56 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/cloudflare/workflow.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing Cloudflare Workflows with Alchemy
3 | description: Learn how to create and manage Cloudflare Workflows using Alchemy to orchestrate and automate tasks.
4 | ---
5 |
6 | # Workflow
7 |
8 | A [Cloudflare Workflow](https://developers.cloudflare.com/workers/configuration/workflows/) allows you to define reusable logic that can be shared across multiple Workers.
9 |
10 | ## Minimal Example
11 |
12 | Create a basic workflow that can be bound to a Worker.
13 |
14 | ```ts
15 | import { Workflow } from "alchemy/cloudflare";
16 |
17 | const workflow = await Workflow("my-workflow", {
18 | workflowName: "my-workflow",
19 | className: "MyWorkflow"
20 | });
21 | ```
22 |
23 | ## Use a Workflow Defined in Another Script
24 |
25 | Reference a workflow implemented in a different worker script using `scriptName`.
26 |
27 | ```ts
28 | import { Workflow } from "alchemy/cloudflare";
29 |
30 | const workflow = await Workflow("shared-workflow", {
31 | workflowName: "my-workflow",
32 | className: "MyWorkflow",
33 | scriptName: "shared-worker"
34 | });
35 | ```
36 |
37 | ## Bind to a Worker
38 |
39 | Bind a workflow to a Worker to use its functionality.
40 |
41 | ```ts
42 | import { Worker, Workflow } from "alchemy/cloudflare";
43 |
44 | const workflow = await Workflow("my-workflow", {
45 | workflowName: "my-workflow",
46 | className: "MyWorkflow"
47 | });
48 |
49 | await Worker("my-worker", {
50 | name: "my-worker",
51 | script: "console.log('Hello, world!')",
52 | bindings: {
53 | WORKFLOW: workflow
54 | }
55 | });
56 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/dns/import-dns.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Importing DNS Records into Alchemy
3 | description: Learn how to import existing DNS records from your provider into Alchemy for management with Infrastructure-as-Code.
4 | ---
5 |
6 | # ImportDnsRecords
7 |
8 | The ImportDnsRecords resource lets you import DNS records from any domain using [Cloudflare's DNS-over-HTTPS API](https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/).
9 |
10 | ## Minimal Example
11 |
12 | Import all default DNS record types for a domain.
13 |
14 | ```ts
15 | import { ImportDnsRecords } from "alchemy/dns";
16 |
17 | const dnsRecords = await ImportDnsRecords("example-com", {
18 | domain: "example.com"
19 | });
20 | ```
21 |
22 | ## Import Specific Record Types
23 |
24 | Import only specified DNS record types.
25 |
26 | ```ts
27 | import { ImportDnsRecords } from "alchemy/dns";
28 |
29 | const records = await ImportDnsRecords("example-com", {
30 | domain: "example.com",
31 | recordTypes: ["A", "MX"]
32 | });
33 | ```
34 |
35 | ## Transfer Records to Cloudflare
36 |
37 | Import DNS records and transfer them to a Cloudflare zone.
38 |
39 | ```ts
40 | import { ImportDnsRecords, DnsRecords } from "alchemy/dns";
41 |
42 | const dnsRecords = await ImportDnsRecords("dns-records", {
43 | domain: "example.com"
44 | });
45 |
46 | await DnsRecords("transfer-records", {
47 | zoneId: zone.id,
48 | records: dnsRecords.records
49 | });
50 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/esbuild/bundle.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Bundling Code with esbuild in Alchemy
3 | description: Learn how to use Alchemy's esbuild provider to bundle JavaScript and TypeScript code for your serverless functions and web applications.
4 | ---
5 |
6 | # Bundle
7 |
8 | The Bundle resource uses [esbuild](https://esbuild.github.io/) to bundle JavaScript and TypeScript files into optimized output.
9 |
10 | ## Minimal Example
11 |
12 | Bundle a TypeScript file into ESM format:
13 |
14 | ```ts
15 | import { Bundle } from "alchemy/esbuild";
16 |
17 | const bundle = await Bundle("handler", {
18 | entryPoint: "src/handler.ts",
19 | format: "esm"
20 | });
21 | ```
22 |
23 | ## Bundle with Output Directory
24 |
25 | Specify an output directory and additional build options:
26 |
27 | ```ts
28 | const bundle = await Bundle("api", {
29 | entryPoint: "src/api.ts",
30 | outdir: ".build",
31 | format: "esm",
32 | platform: "node",
33 | target: "node18",
34 | minify: true,
35 | sourcemap: true
36 | });
37 | ```
38 |
39 | ## Bundle with External Dependencies
40 |
41 | Exclude packages from the bundle:
42 |
43 | ```ts
44 | const bundle = await Bundle("app", {
45 | entryPoint: "src/app.ts",
46 | format: "esm",
47 | external: ["aws-sdk", "lodash"],
48 | platform: "node"
49 | });
50 | ```
51 |
52 | ## Bundle for Browser
53 |
54 | Create a browser-compatible IIFE bundle:
55 |
56 | ```ts
57 | const bundle = await Bundle("web", {
58 | entryPoint: "src/main.ts",
59 | outfile: "dist/bundle.js",
60 | format: "iife",
61 | platform: "browser",
62 | minify: true,
63 | sourcemap: "external"
64 | });
65 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/fs/copy-file.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Copying Files with Alchemy FS Provider
3 | description: Learn how to copy files and directories within your project using Alchemy's FS (File System) provider.
4 | ---
5 |
6 | # CopyFile
7 |
8 | The CopyFile resource lets you copy files from one location to another in the filesystem.
9 |
10 | ## Minimal Example
11 |
12 | Copy a file to a new location:
13 |
14 | ```ts
15 | import { CopyFile } from "alchemy/fs";
16 |
17 | const copiedFile = await CopyFile("config-copy", {
18 | src: "config.json",
19 | dest: "backup/config.json"
20 | });
21 | ```
22 |
23 | ## Copy Without Overwriting
24 |
25 | Copy a file only if the destination doesn't already exist:
26 |
27 | ```ts
28 | import { CopyFile } from "alchemy/fs";
29 |
30 | const safeCopy = await CopyFile("safe-copy", {
31 | src: "data.json",
32 | dest: "backup/data.json",
33 | overwrite: false
34 | });
35 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/fs/folder.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing Folders (Directories) with Alchemy FS Provider
3 | description: Learn how to create, manage, and delete folders (directories) using Alchemy's FS (File System) provider.
4 | ---
5 |
6 | # Folder
7 |
8 | The Folder resource creates and manages directories in the filesystem with automatic parent directory creation and cleanup on deletion.
9 |
10 | ## Minimal Example
11 |
12 | Create a directory using the ID as the path:
13 |
14 | ```ts
15 | import { Folder } from "alchemy/fs";
16 |
17 | const dir = await Folder("uploads");
18 | ```
19 |
20 | ## Custom Path
21 |
22 | Create a directory with an explicit path:
23 |
24 | ```ts
25 | import { Folder } from "alchemy/fs";
26 |
27 | const logs = await Folder("logs", {
28 | path: "var/log/app"
29 | });
30 | ```
31 |
32 | ## Recursive Creation
33 |
34 | Create nested directories with recursive creation enabled (default):
35 |
36 | ```ts
37 | import { Folder } from "alchemy/fs";
38 |
39 | const nested = await Folder("nested", {
40 | path: "path/to/nested/dir",
41 | recursive: true
42 | });
43 | ```
44 |
45 | ## Cleanup Options
46 |
47 | Control folder deletion behavior:
48 |
49 | ```ts
50 | import { Folder } from "alchemy/fs";
51 |
52 | const temp = await Folder("temp", {
53 | path: "temp",
54 | delete: true, // Delete on destroy (default)
55 | clean: true // Remove contents on delete
56 | });
57 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/fs/static-css-file.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing Static CSS Files with Alchemy FS Provider
3 | description: Learn how to create and manage static CSS (.css) files with proper formatting using Alchemy's FS provider.
4 | ---
5 |
6 | # StaticCSSFile
7 |
8 | The StaticCSSFile resource creates and manages static CSS files in your project using [Alchemy's File System](https://alchemy.run/docs/concepts/fs) capabilities.
9 |
10 | ## Minimal Example
11 |
12 | Creates a basic CSS file with styles:
13 |
14 | ```ts
15 | import { StaticCSSFile } from "alchemy/fs";
16 |
17 | const styles = await StaticCSSFile("styles.css", `
18 | .container {
19 | max-width: 1200px;
20 | margin: 0 auto;
21 | padding: 0 1rem;
22 | }
23 | `);
24 | ```
25 |
26 | ## Custom Path
27 |
28 | Creates a CSS file at a specific path:
29 |
30 | ```ts
31 | import { StaticCSSFile } from "alchemy/fs";
32 |
33 | const styles = await StaticCSSFile("main",
34 | "src/styles/main.css",
35 | `.button {
36 | background-color: #0062ff;
37 | color: white;
38 | border: none;
39 | padding: 0.5rem 1rem;
40 | border-radius: 4px;
41 | }`
42 | );
43 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/fs/static-html-file.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing Static HTML Files with Alchemy FS Provider
3 | description: Learn how to create and manage static HTML (.html) files with proper formatting using Alchemy's FS provider.
4 | ---
5 |
6 | # StaticHTMLFile
7 |
8 | The StaticHTMLFile resource creates static HTML files with automatic directory creation and cleanup.
9 |
10 | ## Minimal Example
11 |
12 | Creates a basic HTML file:
13 |
14 | ```ts
15 | import { StaticHTMLFile } from "alchemy/fs";
16 |
17 | const page = await StaticHTMLFile("index.html", `
18 |
19 |
20 | My Page
21 | Hello World
22 |
23 | `);
24 | ```
25 |
26 | ## Custom Path
27 |
28 | Creates an HTML file at a specific path:
29 |
30 | ```ts
31 | import { StaticHTMLFile } from "alchemy/fs";
32 |
33 | const page = await StaticHTMLFile("home", "pages/index.html", `
34 |
35 |
36 |
37 | Home
38 |
39 |
40 |
41 | Welcome
42 | This is my homepage.
43 |
44 |
45 | `);
46 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/fs/static-text-file.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing Static Text Files with Alchemy FS Provider
3 | description: Learn how to create and manage static text (.txt) files using Alchemy's FS provider.
4 | ---
5 |
6 | # StaticTextFile
7 |
8 | The StaticTextFile resource creates and manages plain text files in the filesystem using [Alchemy's File System](https://alchemy.run/docs/concepts/fs) capabilities.
9 |
10 | ## Minimal Example
11 |
12 | Creates a simple text file with content:
13 |
14 | ```ts
15 | import { StaticTextFile } from "alchemy/fs";
16 |
17 | const readme = await StaticTextFile("README.md",
18 | "# Project Name\n\nProject description goes here."
19 | );
20 | ```
21 |
22 | ## Custom Path
23 |
24 | Creates a text file at a specific path:
25 |
26 | ```ts
27 | import { StaticTextFile } from "alchemy/fs";
28 |
29 | const changelog = await StaticTextFile("CHANGELOG.md",
30 | "docs/CHANGELOG.md",
31 | "# Changelog\n\n## v1.0.0\n\n- Initial release"
32 | );
33 | ```
34 |
35 | ## Nested Directory
36 |
37 | Creates a text file in a nested directory structure (directories are created automatically):
38 |
39 | ```ts
40 | import { StaticTextFile } from "alchemy/fs";
41 |
42 | const log = await StaticTextFile("app.log",
43 | "logs/app/app.log",
44 | "Application started successfully"
45 | );
46 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/fs/static-vue-file.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing Static Vue Files with Alchemy FS Provider
3 | description: Learn how to create and manage static Vue (.vue) single-file components with proper formatting using Alchemy's FS provider.
4 | ---
5 |
6 | # StaticVueFile
7 |
8 | The StaticVueFile resource creates [Vue.js](https://vuejs.org/) single-file component files (.vue) with template, script and style sections.
9 |
10 | ## Minimal Example
11 |
12 | Creates a basic Vue component file with template, script and style sections.
13 |
14 | ```ts
15 | import { StaticVueFile } from "alchemy/fs";
16 |
17 | const button = await StaticVueFile("Button.vue", `
18 |
19 |
20 |
21 |
22 |
29 |
30 |
35 | `);
36 | ```
37 |
38 | ## Custom Path
39 |
40 | Creates a Vue component file at a specific path.
41 |
42 | ```ts
43 | import { StaticVueFile } from "alchemy/fs";
44 |
45 | const header = await StaticVueFile("Header",
46 | "components/Header.vue",
47 | `
48 |
49 | {{ title }}
50 |
53 |
54 |
55 |
56 |
63 | `);
64 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/fs/static-yaml-file.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing Static YAML Files with Alchemy FS Provider
3 | description: Learn how to create and manage static YAML (.yaml, .yml) files with proper formatting using Alchemy's FS provider.
4 | ---
5 |
6 | # StaticYamlFile
7 |
8 | The StaticYamlFile resource creates YAML files with formatted content using the [YAML](https://yaml.org/) format.
9 |
10 | ## Minimal Example
11 |
12 | Creates a simple YAML configuration file.
13 |
14 | ```ts
15 | import { StaticYamlFile } from "alchemy/fs";
16 |
17 | const config = await StaticYamlFile("config.yaml", {
18 | server: {
19 | host: "localhost",
20 | port: 3000
21 | }
22 | });
23 | ```
24 |
25 | ## Nested Configuration
26 |
27 | Creates a YAML file with nested configuration objects.
28 |
29 | ```ts
30 | import { StaticYamlFile } from "alchemy/fs";
31 |
32 | const config = await StaticYamlFile("config.yaml", {
33 | server: {
34 | host: "localhost",
35 | port: 3000
36 | },
37 | database: {
38 | url: "postgresql://localhost:5432/db",
39 | pool: {
40 | min: 1,
41 | max: 10
42 | }
43 | }
44 | });
45 | ```
46 |
47 | ## Custom Path
48 |
49 | Creates a YAML file at a specific path.
50 |
51 | ```ts
52 | import { StaticYamlFile } from "alchemy/fs";
53 |
54 | const config = await StaticYamlFile("config", "config/app.yaml", {
55 | environment: "production",
56 | features: ["auth", "api", "storage"]
57 | });
58 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/neon/project.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing Neon Serverless Postgres Projects with Alchemy
3 | description: Learn how to create, configure, and manage Neon serverless Postgres projects and databases using Alchemy.
4 | ---
5 |
6 | # NeonProject
7 |
8 | The NeonProject resource lets you create and manage [Neon serverless PostgreSQL](https://neon.tech) projects.
9 |
10 | ## Minimal Example
11 |
12 | Create a basic Neon project with default settings:
13 |
14 | ```ts
15 | import { NeonProject } from "alchemy/neon";
16 |
17 | const project = await NeonProject("my-project", {
18 | name: "My Project"
19 | });
20 | ```
21 |
22 | ## Custom Region and Version
23 |
24 | Create a project in a specific region with a specific PostgreSQL version:
25 |
26 | ```ts
27 | import { NeonProject } from "alchemy/neon";
28 |
29 | const project = await NeonProject("eu-project", {
30 | name: "EU Project",
31 | region_id: "aws-eu-west-1",
32 | pg_version: 16,
33 | apiKey: alchemy.secret(process.env.NEON_API_KEY)
34 | });
35 | ```
36 |
37 | ## Custom Branch Name
38 |
39 | Create a project with a custom default branch name:
40 |
41 | ```ts
42 | import { NeonProject } from "alchemy/neon";
43 |
44 | const project = await NeonProject("dev-project", {
45 | name: "Development Project",
46 | default_branch_name: "development"
47 | });
48 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/sentry/project.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing Sentry Projects with Alchemy
3 | description: Learn how to create, configure, and manage Sentry projects using Alchemy.
4 | ---
5 |
6 | # Sentry Project
7 |
8 | Create and manage Sentry projects.
9 |
10 | ## Authentication
11 |
12 | You can authenticate with Sentry in two ways:
13 |
14 | 1. Environment variable (recommended):
15 | ```bash
16 | # .env
17 | SENTRY_AUTH_TOKEN=your_auth_token
18 | ```
19 |
20 | 2. Pass the token directly:
21 | ```typescript
22 | const project = await Project("my-project", {
23 | authToken: alchemy.secret(process.env.SENTRY_AUTH_TOKEN),
24 | name: "My Project",
25 | organization: "my-org",
26 | team: "my-team",
27 | });
28 | ```
29 |
30 | Get your auth token from [Sentry's API settings](https://sentry.io/settings/account/api/auth-tokens/).
31 |
32 | ## Examples
33 |
34 | ### Minimal
35 |
36 | ```typescript
37 | import { Project } from "alchemy/sentry";
38 |
39 | const project = await Project("my-project", {
40 | name: "My Project",
41 | organization: "my-org"
42 | team: "my-team",
43 | });
44 | ```
45 |
46 | ### Adopt
47 |
48 | For when you have an existing Project in Sentry already.
49 |
50 | ```ts
51 | const project = await Project("my-project", {
52 | adopt: true,
53 | name: "My Project",
54 | organization: "my-org",
55 | team: "my-team",
56 | })
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/sentry/team.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing Sentry Teams with Alchemy
3 | description: Learn how to create, configure, and manage Sentry teams using Alchemy.
4 | ---
5 |
6 | # Sentry Team
7 |
8 | Create and manage Sentry teams.
9 |
10 | ## Authentication
11 |
12 | You can authenticate with Sentry in two ways:
13 |
14 | 1. Environment variable (recommended):
15 | ```bash
16 | # .env
17 | SENTRY_AUTH_TOKEN=your_auth_token
18 | ```
19 |
20 | 2. Pass the token directly:
21 | ```typescript
22 | const team = await Team("my-team", {
23 | authToken: alchemy.secret(process.env.SENTRY_AUTH_TOKEN),
24 | name: "My Team",
25 | organization: "my-org",
26 | });
27 | ```
28 |
29 | Get your auth token from [Sentry's API settings](https://sentry.io/settings/account/api/auth-tokens/).
30 |
31 | ## Examples
32 |
33 | ### Minimal
34 |
35 | ```typescript
36 | import { Team } from "alchemy/sentry";
37 |
38 | const team = await Team("my-team", {
39 | name: "My Team",
40 | organization: "my-org",
41 | });
42 | ```
43 |
44 | ### Adopt
45 |
46 | For when you have an existing Team within Sentry:
47 |
48 | ```typescript
49 | import { Team } from "alchemy/sentry";
50 |
51 | const team = await Team("my-team", {
52 | adopt: true,
53 | name: "My Team",
54 | organization: "my-org",
55 | });
56 | ```
57 |
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/stripe/price.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing Stripe Prices with Alchemy
3 | description: Learn how to create and manage Stripe Prices for your products and subscriptions using Alchemy.
4 | ---
5 |
6 | # Price
7 |
8 | The Price resource lets you create and manage [Stripe Prices](https://stripe.com/docs/api/prices) for products.
9 |
10 | ## Minimal Example
11 |
12 | Create a one-time fixed price for a product:
13 |
14 | ```ts
15 | import { Price } from "alchemy/stripe";
16 |
17 | const price = await Price("basic-license", {
18 | currency: "usd",
19 | unitAmount: 2999, // $29.99
20 | product: "prod_xyz"
21 | });
22 | ```
23 |
24 | ## Recurring Subscription Price
25 |
26 | Create a recurring subscription price with fixed monthly billing:
27 |
28 | ```ts
29 | import { Price } from "alchemy/stripe";
30 |
31 | const subscriptionPrice = await Price("pro-monthly", {
32 | currency: "usd",
33 | unitAmount: 1499, // $14.99/month
34 | product: "prod_xyz",
35 | recurring: {
36 | interval: "month",
37 | usageType: "licensed"
38 | }
39 | });
40 | ```
41 |
42 | ## Metered Usage Price
43 |
44 | Create a metered price for usage-based billing:
45 |
46 | ```ts
47 | import { Price } from "alchemy/stripe";
48 |
49 | const meteredPrice = await Price("storage", {
50 | currency: "usd",
51 | unitAmount: 25, // $0.25 per GB
52 | product: "prod_xyz",
53 | recurring: {
54 | interval: "month",
55 | usageType: "metered",
56 | aggregateUsage: "sum"
57 | }
58 | });
59 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/stripe/product.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Managing Stripe Products with Alchemy
3 | description: Learn how to create and manage Stripe Products and SKUs using Alchemy for your e-commerce or subscription service.
4 | ---
5 |
6 | # Product
7 |
8 | The Product resource lets you create and manage [Stripe Products](https://stripe.com/docs/api/products) for your Stripe account.
9 |
10 | ## Minimal Example
11 |
12 | Create a basic digital product:
13 |
14 | ```ts
15 | import { Product } from "alchemy/stripe";
16 |
17 | const product = await Product("basic-software", {
18 | name: "Basic Software License",
19 | description: "Single-user license for basic software package"
20 | });
21 | ```
22 |
23 | ## Physical Product
24 |
25 | Create a physical product with shipping details:
26 |
27 | ```ts
28 | import { Product } from "alchemy/stripe";
29 |
30 | const product = await Product("premium-hardware", {
31 | name: "Premium Hardware Kit",
32 | description: "Complete hardware kit with premium components",
33 | shippable: true,
34 | images: ["https://example.com/hardware-kit.jpg"],
35 | unitLabel: "kit",
36 | statementDescriptor: "PREMIUM HW KIT"
37 | });
38 | ```
39 |
40 | ## Service Product
41 |
42 | Create a service product with tax code:
43 |
44 | ```ts
45 | import { Product } from "alchemy/stripe";
46 |
47 | const product = await Product("consulting", {
48 | name: "Professional Consulting",
49 | description: "Expert consulting services",
50 | type: "service",
51 | taxCode: "txcd_10000000",
52 | metadata: {
53 | industry: "technology",
54 | expertise: "cloud"
55 | }
56 | });
57 | ```
--------------------------------------------------------------------------------
/alchemy-web/docs/providers/vercel/project-domain.md:
--------------------------------------------------------------------------------
1 | # ProjectDomain
2 |
3 | Add and manage domains for Vercel projects.
4 |
5 | ## Authentication
6 |
7 | To use this resource, you must authenticate with Vercel. You can do this in one of two ways:
8 |
9 | 1. **Environment Variable:** Set `VERCEL_ACCESS_TOKEN` in your `.env` file.
10 | 2. **Direct Prop:** Pass `accessToken` directly in your resource configuration.
11 |
12 | ## Examples
13 |
14 | ### Minimal
15 |
16 | ```ts
17 | const domain = await ProjectDomain("my-app.com", {
18 | name: "my-app.com",
19 | project: "prj_123",
20 | });
21 | ```
22 |
23 | ### With `accessToken`
24 |
25 | ```ts
26 | const domain = await ProjectDomain("my-app.com", {
27 | accessToken: alchemy.secret(process.env.VERCEL_ACCESS_TOKEN),
28 | name: "my-app.com",
29 | project: "prj_123",
30 | });
31 | ```
32 |
33 | ### With `redirect`
34 |
35 | ```ts
36 | const domain = await ProjectDomain("my-app.com", {
37 | name: "my-app.com",
38 | project: "prj_123",
39 | gitBranch: "main",
40 | redirect: "https://example.com",
41 | redirectStatusCode: 301,
42 | });
43 | ```
44 |
--------------------------------------------------------------------------------
/alchemy-web/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: home
3 | title: Alchemy
4 | description: TypeScript-native Infrastructure-as-Code without the dead weight. Create, Update, Delete resources with pure async TypeScript.
5 |
6 | # Custom hero component instead of the default hero
7 | ---
8 |
9 |
17 |
18 |
19 | ```typescript
20 | const database = await D1Database("my-app-db", {
21 | name: "my-application-db"
22 | });
23 |
24 | const site = await Worker("website", {
25 | name: "my-app",
26 | bindings: {
27 | DB: database
28 | }
29 | });
30 |
31 | const product = await Product("pro-plan", {
32 | name: "Pro Plan",
33 | description: "Professional subscription tier"
34 | });
35 | ```
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/alchemy-web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "alchemy-web",
3 | "scripts": {
4 | "docs:gen": "bun alchemy.run.ts",
5 | "docs:dev": "vitepress dev",
6 | "docs:build": "NODE_OPTIONS='--max-old-space-size=8192' vitepress build",
7 | "docs:preview": "vitepress preview"
8 | },
9 | "dependencies": {
10 | "sharp": "^0.34.1",
11 | "vitepress-plugin-group-icons": "^1.5.2"
12 | },
13 | "devDependencies": {
14 | "@shikijs/vitepress-twoslash": "^3.4.0",
15 | "markdown-it-footnote": "^4.0.0",
16 | "vitepress": "^1.6.3",
17 | "vue": "^3.5.13"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/alchemy-web/public/alchemist.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/alchemy-web/public/alchemist.webp
--------------------------------------------------------------------------------
/alchemy-web/public/alchemy-flower.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/alchemy-web/public/alchemy-flower.png
--------------------------------------------------------------------------------
/alchemy-web/public/alchemy-og.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/alchemy-web/public/alchemy-og.png
--------------------------------------------------------------------------------
/alchemy-web/public/potion-with-border.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/alchemy-web/public/potion-with-border.png
--------------------------------------------------------------------------------
/alchemy-web/public/potion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/alchemy-web/public/potion.png
--------------------------------------------------------------------------------
/alchemy/src/ai/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./ark.ts";
2 | export * from "./astro-file.ts";
3 | export * from "./css-file.ts";
4 | export * from "./data.ts";
5 | export * from "./document.ts";
6 | export * from "./html-file.ts";
7 | export * from "./json-file.ts";
8 | export * from "./typescript-file.ts";
9 | export * from "./vue-file.ts";
10 | export * from "./yaml-file.ts";
11 |
--------------------------------------------------------------------------------
/alchemy/src/aws/account-id.ts:
--------------------------------------------------------------------------------
1 | import { GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
2 |
3 | const sts = new STSClient({});
4 |
5 | export type AccountId = string & {
6 | readonly __brand: "AccountId";
7 | };
8 |
9 | /**
10 | * Helper to get the current AWS account ID
11 | */
12 | export async function AccountId(): Promise {
13 | const identity = await sts.send(new GetCallerIdentityCommand({}));
14 | return identity.Account! as AccountId;
15 | }
16 |
--------------------------------------------------------------------------------
/alchemy/src/aws/control/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./client.ts";
2 | export * from "./proxy.ts";
3 | export * from "./resource.ts";
4 | import { AWS } from "./proxy.ts";
5 |
6 | export default AWS;
7 |
--------------------------------------------------------------------------------
/alchemy/src/aws/control/proxy.ts:
--------------------------------------------------------------------------------
1 | import { createResourceType } from "./resource.ts";
2 |
3 | /**
4 | * Proxy-based interface for dynamically accessing AWS resource handlers.
5 | * This allows for a natural, namespace-like access pattern (e.g., AWS.S3.Bucket)
6 | * while lazily creating resource handlers as needed.
7 | */
8 | export const AWS = new Proxy(
9 | {},
10 | {
11 | get(_, serviceName: string) {
12 | // Skip internal properties
13 | if (typeof serviceName !== "string" || serviceName.startsWith("_")) {
14 | return undefined;
15 | }
16 |
17 | // Create a nested proxy for the service namespace
18 | return new Proxy(
19 | {},
20 | {
21 | get(_, resourceName: string) {
22 | // Skip internal properties
23 | if (
24 | typeof resourceName !== "string" ||
25 | resourceName.startsWith("_")
26 | ) {
27 | return undefined;
28 | }
29 |
30 | // Construct the CloudFormation resource type name
31 | const typeName = `AWS::${serviceName}::${resourceName}`;
32 |
33 | // Return the memoized resource handler
34 | return createResourceType(typeName);
35 | },
36 | },
37 | );
38 | },
39 | },
40 | ) as typeof import("./types");
41 |
--------------------------------------------------------------------------------
/alchemy/src/aws/credentials.ts:
--------------------------------------------------------------------------------
1 | import type { Secret } from "../secret.ts";
2 |
3 | export interface AwsCredentials {
4 | accessKeyId: Secret;
5 | secretAccessKey: Secret;
6 | }
7 |
--------------------------------------------------------------------------------
/alchemy/src/aws/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./account-id.ts";
2 | export * from "./bucket.ts";
3 | export * from "./function.ts";
4 | export * from "./policy-attachment.ts";
5 | export * from "./policy.ts";
6 | export * from "./queue.ts";
7 | export * from "./role.ts";
8 | export * from "./ses.ts";
9 | export * from "./ssm-parameter.ts";
10 | export * from "./table.ts";
11 |
--------------------------------------------------------------------------------
/alchemy/src/aws/oidc/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./github-oidc-provider.ts";
2 | export * from "./oidc-provider.ts";
3 |
--------------------------------------------------------------------------------
/alchemy/src/aws/retry.ts:
--------------------------------------------------------------------------------
1 | import { withExponentialBackoff } from "../util/retry.ts";
2 |
3 | /**
4 | * Check if an error is retryable (throttling/rate limiting)
5 | */
6 | function isRetryableError(error: any): boolean {
7 | if (!error) return false;
8 |
9 | const errorCode = error.name || error.code || "";
10 | const errorMessage = error.message || "";
11 |
12 | // Check for common AWS throttling error codes and messages
13 | return (
14 | errorCode === "Throttling" ||
15 | errorCode === "ThrottlingException" ||
16 | errorCode === "TooManyRequestsException" ||
17 | errorCode === "RequestLimitExceeded" ||
18 | errorMessage.includes("Rate exceeded") ||
19 | errorMessage.includes("throttling") ||
20 | errorMessage.includes("Throttling") ||
21 | errorMessage.includes("Internal server error")
22 | );
23 | }
24 |
25 | /**
26 | * Retry function with standardized parameters for AWS throttling
27 | */
28 | export function retry(
29 | operation: () => Promise,
30 | extraIsRetryableError?: (error: any) => boolean,
31 | ): Promise {
32 | return withExponentialBackoff(
33 | operation,
34 | (err) => isRetryableError(err) || extraIsRetryableError?.(err) || false,
35 | 10, // max attempts
36 | 1000, // initial delay ms
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/alchemy/src/cloudflare/ai.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @see https://developers.cloudflare.com/workers-ai/
3 | */
4 | export class Ai {
5 | public readonly type = "ai";
6 |
7 | /**
8 | * @internal
9 | */
10 | ///@ts-ignore
11 | _phantom: Models;
12 | }
13 |
--------------------------------------------------------------------------------
/alchemy/src/cloudflare/analytics-engine.ts:
--------------------------------------------------------------------------------
1 | export interface AnalyticsEngineDatasetProps {
2 | /**
3 | * The name of the dataset to bind to -
4 | * becomes the table name to query via the api in the FROM clause
5 | */
6 | dataset: string;
7 | }
8 |
9 | /**
10 | * @example
11 | * // Create a binding for an Analytics Engine dataset
12 | * const dataset = new AnalyticsEngineDataset("ae-dataset", {
13 | * dataset: "WEATHER",
14 | * });
15 | */
16 | export class AnalyticsEngineDataset {
17 | public readonly type = "analytics_engine" as const;
18 | public readonly dataset: string;
19 |
20 | constructor(
21 | public readonly id: string,
22 | input: AnalyticsEngineDatasetProps,
23 | ) {
24 | this.dataset = input.dataset;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/alchemy/src/cloudflare/asset-manifest.ts:
--------------------------------------------------------------------------------
1 | export type AssetManifest = AssetManifestEntry[];
2 |
3 | export interface AssetManifestEntry {
4 | source: string;
5 | key: string;
6 | hash: string;
7 | cacheControl: string;
8 | contentType?: string;
9 | }
10 |
11 | export interface FileOption {
12 | files: string | string[];
13 | cacheControl: string;
14 | contentType?: string;
15 | ignore?: string[];
16 | }
17 |
--------------------------------------------------------------------------------
/alchemy/src/cloudflare/browser-rendering.ts:
--------------------------------------------------------------------------------
1 | export class BrowserRendering {
2 | public readonly type = "browser";
3 | }
4 |
--------------------------------------------------------------------------------
/alchemy/src/cloudflare/bundle/external.ts:
--------------------------------------------------------------------------------
1 | // https://developers.cloudflare.com/workers/runtime-apis/nodejs/#supported-nodejs-apis
2 | const nodejs_compat = [
3 | "node:async_hooks",
4 | "node:assert",
5 | "node:buffer",
6 | "node:console",
7 | "node:crypto",
8 | "node:debug",
9 | "node:diagnostics_channel",
10 | "node:dns",
11 | "node:events",
12 | "node:inspector",
13 | "node:net",
14 | "node:path",
15 | "node:perf_hooks", // partially supported
16 | "node:process",
17 | "node:querystring",
18 | "node:stream",
19 | "node:string_decoder",
20 | "node:timers",
21 | "node:tls", // partially supported
22 | "node:url",
23 | "node:util",
24 | "node:zlib",
25 | // "node:*",
26 | ];
27 |
28 | export const external = [
29 | ...nodejs_compat,
30 | ...nodejs_compat.map((p) => p.split(":")[1]),
31 | "cloudflare:workers",
32 | "cloudflare:workflows",
33 | "cloudflare:*",
34 | ];
35 |
36 | export const external_als = [
37 | //
38 | "node:async_hooks",
39 | "async_hooks",
40 | "cloudflare:*",
41 | ];
42 |
--------------------------------------------------------------------------------
/alchemy/src/cloudflare/bundle/local-dev-cloudflare-shim.ts:
--------------------------------------------------------------------------------
1 | import { dedent } from "../../util/dedent.ts";
2 |
3 | /**
4 | * TanStackStart server functions and middleware run in Node.js intead of Miniflare when using `vite dev`.
5 | *
6 | * This plugin polyfills the cloudflare:workers module during the dev server phase.
7 | */
8 | export function cloudflareWorkersDevEnvironmentShim() {
9 | return {
10 | name: "cloudflare-workers-dev-shim",
11 | apply: "serve", // dev‑only
12 | enforce: "pre",
13 | resolveId(id: string) {
14 | if (id === "cloudflare:workers") return id; // tell Vite we handled it
15 | },
16 | load(id: string) {
17 | if (id === "cloudflare:workers")
18 | return dedent`
19 | import { getPlatformProxy } from "wrangler";
20 | // TODO: should we export default cloudflare; ??
21 | const cloudflare = await getPlatformProxy();
22 | export const env = cloudflare.env;`;
23 | },
24 | } as const;
25 | }
26 |
--------------------------------------------------------------------------------
/alchemy/src/cloudflare/event-source.ts:
--------------------------------------------------------------------------------
1 | import type { QueueConsumerSettings } from "./queue-consumer.ts";
2 | import type { QueueResource } from "./queue.ts";
3 |
4 | /**
5 | * Base interface for event sources that can be bound to a Worker
6 | */
7 | export type EventSource = QueueEventSource | QueueResource;
8 |
9 | /**
10 | * Configuration for a Queue as an event source for a Worker
11 | */
12 | export interface QueueEventSource {
13 | /**
14 | * The queue to consume messages from
15 | */
16 | readonly queue: QueueResource;
17 |
18 | /**
19 | * Optional settings for configuring how the Worker consumes the queue
20 | */
21 | readonly settings?: QueueConsumerSettings;
22 | }
23 |
24 | /**
25 | * Checks if an event source is a QueueEventSource
26 | * @param eventSource - The event source to check
27 | * @returns true if the event source is a QueueEventSource, false otherwise
28 | */
29 | export function isQueueEventSource(
30 | eventSource: any,
31 | ): eventSource is QueueEventSource {
32 | return "queue" in eventSource;
33 | }
34 |
--------------------------------------------------------------------------------
/alchemy/src/cloudflare/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./account-api-token.ts";
2 | export * from "./account-id.ts";
3 | export * from "./ai-gateway.ts";
4 | export * from "./ai.ts";
5 | export * from "./analytics-engine.ts";
6 | export * from "./api-error.ts";
7 | export * from "./api.ts";
8 | export * from "./assets.ts";
9 | export * from "./bindings.ts";
10 | export * from "./bound.ts";
11 | export * from "./browser-rendering.ts";
12 | export * from "./bucket.ts";
13 | export * from "./bundle/external.ts";
14 | export * from "./bundle/local-dev-cloudflare-shim.ts";
15 | export * from "./custom-domain.ts";
16 | export * from "./d1-clone.ts";
17 | export * from "./d1-database.ts";
18 | export * from "./d1-export.ts";
19 | export * from "./d1-import.ts";
20 | export * from "./dns-records.ts";
21 | export * from "./durable-object-namespace.ts";
22 | export * from "./hyperdrive.ts";
23 | export * from "./kv-namespace.ts";
24 | export * from "./nuxt.ts";
25 | export * from "./permission-groups.ts";
26 | export * from "./pipeline.ts";
27 | export * from "./queue-consumer.ts";
28 | export * from "./queue.ts";
29 | export * from "./r2-rest-state-store.ts";
30 | export * from "./react-router.ts";
31 | export * from "./redwood.ts";
32 | export * from "./route.ts";
33 | export * from "./tanstack-start.ts";
34 | export * from "./vectorize-index.ts";
35 | export * from "./vectorize-metadata-index.ts";
36 | export * from "./version-metadata.ts";
37 | export * from "./vite.ts";
38 | export * from "./website.ts";
39 | export * from "./worker.ts";
40 | export { Workflow } from "./workflow.ts";
41 | export * from "./wrangler.json.ts";
42 | export * from "./zone.ts";
43 |
--------------------------------------------------------------------------------
/alchemy/src/cloudflare/response.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloudflare API response format
3 | */
4 | export interface CloudflareResponse {
5 | result: T;
6 | success: boolean;
7 | errors: Array<{ code: number; message: string }>;
8 | messages: string[];
9 | }
10 |
--------------------------------------------------------------------------------
/alchemy/src/cloudflare/tanstack-start.ts:
--------------------------------------------------------------------------------
1 | import type { Assets } from "./assets.ts";
2 | import type { Bindings } from "./bindings.ts";
3 | import { Website, type WebsiteProps } from "./website.ts";
4 | import type { Worker } from "./worker.ts";
5 |
6 | export interface TanStackStartProps
7 | extends WebsiteProps {}
8 |
9 | // don't allow the ASSETS to be overriden
10 | export type TanStackStart = B extends { ASSETS: any }
11 | ? never
12 | : Worker;
13 |
14 | export async function TanStackStart(
15 | id: string,
16 | props?: Partial>,
17 | ): Promise> {
18 | return Website(id, {
19 | ...props,
20 | command: props?.command ?? "bun run build",
21 | wrangler: props?.wrangler ?? true,
22 | main: props?.main ?? ".output/server/index.mjs",
23 | compatibilityFlags: ["nodejs_compat", ...(props?.compatibilityFlags ?? [])],
24 | assets: props?.assets ?? ".output/public",
25 | });
26 | }
27 |
--------------------------------------------------------------------------------
/alchemy/src/cloudflare/types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloudflare API response format
3 | */
4 | export interface CloudflareApiResponse {
5 | /**
6 | * API response result
7 | */
8 | result: T;
9 |
10 | /**
11 | * Success status
12 | */
13 | success: boolean;
14 |
15 | /**
16 | * Error details if success is false
17 | */
18 | errors: CloudflareApiError[];
19 |
20 | /**
21 | * Response messages
22 | */
23 | messages: string[];
24 |
25 | /**
26 | * Result information (typically for paginated results)
27 | */
28 | result_info?: {
29 | page: number;
30 | per_page: number;
31 | total_pages: number;
32 | count: number;
33 | total_count: number;
34 | };
35 | }
36 |
37 | /**
38 | * Cloudflare API error format
39 | */
40 | export interface CloudflareApiError {
41 | /**
42 | * Error code
43 | */
44 | code: number;
45 |
46 | /**
47 | * Error message
48 | */
49 | message: string;
50 | }
51 |
52 | /**
53 | * Helper to extract and handle Cloudflare API errors
54 | *
55 | * @param response Fetch response object
56 | * @returns Formatted error message
57 | */
58 | export async function extractCloudflareError(
59 | response: Response,
60 | ): Promise {
61 | try {
62 | const data = (await response.json()) as CloudflareApiResponse;
63 | if (data.errors && data.errors.length > 0) {
64 | return data.errors.map((e) => `Error ${e.code}: ${e.message}`).join(", ");
65 | }
66 | return `HTTP ${response.status}: ${response.statusText}`;
67 | } catch {
68 | return `HTTP ${response.status}: ${response.statusText}`;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/alchemy/src/cloudflare/version-metadata.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @see https://developers.cloudflare.com/workers/runtime-apis/bindings/version-metadata/
3 | */
4 | export class VersionMetadata {
5 | public readonly type = "version_metadata";
6 | }
7 |
--------------------------------------------------------------------------------
/alchemy/src/cloudflare/vite.ts:
--------------------------------------------------------------------------------
1 | import path from "node:path";
2 | import type { Assets } from "./assets.ts";
3 | import type { Bindings } from "./bindings.ts";
4 | import { Website, type WebsiteProps } from "./website.ts";
5 | import type { Worker } from "./worker.ts";
6 |
7 | export interface ViteProps extends WebsiteProps {}
8 |
9 | // don't allow the ASSETS to be overriden
10 | export type Vite = B extends { ASSETS: any }
11 | ? never
12 | : Worker;
13 |
14 | export async function Vite(
15 | id: string,
16 | props: ViteProps,
17 | ): Promise> {
18 | const defaultAssets = path.join("dist", "client");
19 | return Website(id, {
20 | ...props,
21 | assets:
22 | typeof props.assets === "object"
23 | ? {
24 | dist: props.assets.dist ?? defaultAssets,
25 | }
26 | : (props.assets ?? defaultAssets),
27 | });
28 | }
29 |
--------------------------------------------------------------------------------
/alchemy/src/dns/godaddy.ts:
--------------------------------------------------------------------------------
1 | import { safeFetch } from "../util/safe-fetch.ts";
2 |
3 | export type UpdateNameserversOptions = {
4 | domain: string;
5 | apiKey: string;
6 | apiSecret: string;
7 | nameservers: string[];
8 | };
9 |
10 | export async function updateNameservers(options: UpdateNameserversOptions) {
11 | const url = `https://api.godaddy.com/v1/domains/${options.domain}/nameservers`;
12 |
13 | const response = await safeFetch(url, {
14 | method: "PUT",
15 | headers: {
16 | Authorization: `sso-key ${options.apiKey}:${options.apiSecret}`,
17 | "Content-Type": "application/json",
18 | Accept: "application/json",
19 | },
20 | body: JSON.stringify({
21 | nameservers: options.nameservers,
22 | }),
23 | });
24 |
25 | if (response.ok) {
26 | console.log(`✅ Nameservers updated for ${options.domain}`);
27 | } else {
28 | const error = await response.json();
29 | console.error("❌ Failed to update nameservers:", error);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/alchemy/src/dns/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./import-dns.ts";
2 | export * from "./record.ts";
3 |
--------------------------------------------------------------------------------
/alchemy/src/env.ts:
--------------------------------------------------------------------------------
1 | export interface Env {
2 | [key: string]: Promise;
3 | (name: string, value?: T | undefined, error?: string): Promise;
4 | }
5 |
6 | export const env = new Proxy(_env, {
7 | get: (_, name: string) => _env(name),
8 | apply: (_, __, args: [string, any?, string?]) => _env(...args),
9 | }) as Env;
10 |
11 | async function _env(
12 | name: string,
13 | value?: T | undefined,
14 | error?: string,
15 | ): Promise {
16 | if (value !== undefined) {
17 | return value;
18 | }
19 | const env = await resolveEnv();
20 | if (name in env) {
21 | return env[name] as T;
22 | }
23 | throw new Error(error ?? `Environment variable ${name} is not set`);
24 | }
25 |
26 | async function resolveEnv(): Promise> {
27 | if (typeof process !== "undefined") {
28 | return process.env;
29 | }
30 | try {
31 | const { env } = await import("cloudflare:workers");
32 | return env;
33 | } catch (_error) {}
34 | if (typeof import.meta !== "undefined") {
35 | return import.meta.env;
36 | }
37 | throw new Error("No environment found");
38 | }
39 |
--------------------------------------------------------------------------------
/alchemy/src/esbuild/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./bundle.ts";
2 |
--------------------------------------------------------------------------------
/alchemy/src/fs/file-collection.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Collection of files with their contents
3 | */
4 | export type FileCollection = {
5 | /**
6 | * Type identifier for FileCollection
7 | */
8 | type: "fs::FileCollection";
9 | /**
10 | * Map of relative paths to file contents
11 | */
12 | files: {
13 | [relativePath: string]: string;
14 | };
15 | };
16 |
17 | export function isFileCollection(value: unknown): value is FileCollection {
18 | return (
19 | typeof value === "object" &&
20 | value !== null &&
21 | "type" in value &&
22 | value.type === "fs::FileCollection"
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/alchemy/src/fs/file-ref.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Reference to a file in the filesystem
3 | */
4 | export type FileRef = {
5 | /**
6 | * Type identifier for FileRef
7 | */
8 | kind: "fs::FileRef";
9 | /**
10 | * Path to the file
11 | */
12 | path: string;
13 | };
14 |
15 | export function isFileRef(value: unknown): value is FileRef {
16 | return (
17 | typeof value === "object" &&
18 | value !== null &&
19 | "kind" in value &&
20 | value.kind === "fs::FileRef"
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/alchemy/src/fs/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./copy-file.ts";
2 | export * from "./file-collection.ts";
3 | export * from "./file-ref.ts";
4 | export * from "./file-system-state-store.ts";
5 | export * from "./file.ts";
6 | export * from "./folder.ts";
7 | export * from "./static-astro-file.ts";
8 | export * from "./static-css-file.ts";
9 | export * from "./static-html-file.ts";
10 | export * from "./static-json-file.ts";
11 | export * from "./static-text-file.ts";
12 | export * from "./static-typescript-file.ts";
13 | export * from "./static-vue-file.ts";
14 | export * from "./static-yaml-file.ts";
15 |
--------------------------------------------------------------------------------
/alchemy/src/fs/static-astro-file.ts:
--------------------------------------------------------------------------------
1 | import { File } from "./file.ts";
2 |
3 | export type StaticAstroFile = File;
4 |
5 | /**
6 | * Creates a static Astro component file
7 | *
8 | * @example
9 | * // Create an Astro component file with content
10 | * const header = await StaticAstroFile("Header.astro",
11 | * `---
12 | * import Logo from '../components/Logo.astro';
13 | * const navItems = ['Home', 'About', 'Contact'];
14 | * ---
15 | *
16 | *
26 | *
27 | * `
34 | * );
35 | */
36 | export function StaticAstroFile(
37 | id: string,
38 | ...args: [content: string] | [path: string, content: string]
39 | ): Promise {
40 | const [path, content] = args.length === 1 ? [id, args[0]] : args;
41 | return File(id, {
42 | path,
43 | content,
44 | });
45 | }
46 |
--------------------------------------------------------------------------------
/alchemy/src/fs/static-css-file.ts:
--------------------------------------------------------------------------------
1 | import { File } from "./file.ts";
2 |
3 | export type StaticCSSFile = File;
4 |
5 | /**
6 | * Creates a static CSS file
7 | *
8 | * @example
9 | * // Create a CSS file with styles
10 | * const styles = await StaticCSSFile("styles.css",
11 | * `.container {
12 | * max-width: 1200px;
13 | * margin: 0 auto;
14 | * padding: 0 1rem;
15 | * }
16 | *
17 | * .button {
18 | * background-color: #0062ff;
19 | * color: white;
20 | * border: none;
21 | * padding: 0.5rem 1rem;
22 | * border-radius: 4px;
23 | * }`
24 | * );
25 | */
26 | export function StaticCSSFile(
27 | id: string,
28 | ...args: [content: string] | [path: string, content: string]
29 | ): Promise {
30 | const [path, content] = args.length === 1 ? [id, args[0]] : args;
31 | return File(id, {
32 | path,
33 | content,
34 | });
35 | }
36 |
--------------------------------------------------------------------------------
/alchemy/src/fs/static-html-file.ts:
--------------------------------------------------------------------------------
1 | import { File } from "./file.ts";
2 |
3 | export type StaticHTMLFile = File;
4 |
5 | /**
6 | * Creates a static HTML file
7 | *
8 | * @example
9 | * // Create an HTML file with content
10 | * const page = await StaticHTMLFile("index.html",
11 | * `
12 | *
13 | *
14 | *
15 | *
16 | * My Website
17 | *
18 | *
19 | *
20 | *
21 | * Welcome to My Website
22 | *
23 | *
24 | * This is the main content of the page.
25 | *
26 | *
29 | *
30 | * `
31 | * );
32 | */
33 | export function StaticHTMLFile(
34 | id: string,
35 | ...args: [content: string] | [path: string, content: string]
36 | ): Promise {
37 | const [path, content] = args.length === 1 ? [id, args[0]] : args;
38 | return File(id, {
39 | path,
40 | content,
41 | });
42 | }
43 |
--------------------------------------------------------------------------------
/alchemy/src/fs/static-json-file.ts:
--------------------------------------------------------------------------------
1 | import { File } from "./file.ts";
2 |
3 | /**
4 | * Creates a JSON file with formatted content
5 | *
6 | * @example
7 | * // Create a JSON configuration file
8 | * const config = await StaticJsonFile("config.json", {
9 | * api: {
10 | * endpoint: "https://api.example.com",
11 | * version: "v1"
12 | * },
13 | * features: ["auth", "logging"]
14 | * });
15 | */
16 | export type StaticJsonFile = File;
17 |
18 | export async function StaticJsonFile(
19 | id: string,
20 | ...args: [content: any] | [path: string, content: any]
21 | ): Promise {
22 | const [path, content] = args.length === 1 ? [id, args[0]] : args;
23 | return File(id, {
24 | path,
25 | content: await formatJson(content),
26 | });
27 | }
28 |
29 | export async function formatJson(content: any) {
30 | const prettier = await import("prettier");
31 | return prettier.format(JSON.stringify(content), {
32 | parser: "json",
33 | editor: {
34 | tabWidth: 2,
35 | indentWidth: 2,
36 | },
37 | });
38 | }
39 |
--------------------------------------------------------------------------------
/alchemy/src/fs/static-text-file.ts:
--------------------------------------------------------------------------------
1 | import { File } from "./file.ts";
2 |
3 | /**
4 | * Creates a plain text file
5 | *
6 | * @example
7 | * // Create a text file with content
8 | * const readme = await TextFile("README.md",
9 | * "# Project Name\n\nProject description goes here."
10 | * );
11 | */
12 | export type StaticTextFile = File;
13 |
14 | export function StaticTextFile(
15 | id: string,
16 | ...args: [content: string] | [path: string, content: string]
17 | ): Promise {
18 | const [path, content] = args.length === 1 ? [id, args[0]] : args;
19 | return File(id, {
20 | path,
21 | content,
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/alchemy/src/fs/static-typescript-file.ts:
--------------------------------------------------------------------------------
1 | import { File } from "./file.ts";
2 |
3 | /**
4 | * Creates a TypeScript file with formatted content using prettier
5 | *
6 | * @example
7 | * // Create a TypeScript file
8 | * const component = await StaticTypeScriptFile("Component.ts", `
9 | * interface Props {
10 | * name: string;
11 | * age: number;
12 | * }
13 | *
14 | * export function Component({ name, age }: Props) {
15 | * return Hello {name}, you are {age} years old
;
16 | * }
17 | * `);
18 | */
19 | export type StaticTypeScriptFile = File;
20 |
21 | export async function StaticTypeScriptFile(
22 | id: string,
23 | ...args: [content: string] | [path: string, content: string]
24 | ): Promise {
25 | const [path, content] = args.length === 1 ? [id, args[0]] : args;
26 | const prettier = await import("prettier");
27 | return File(id, {
28 | path,
29 | content: await prettier.format(content, {
30 | parser: "typescript",
31 | editor: {
32 | tabWidth: 2,
33 | indentWidth: 2,
34 | },
35 | }),
36 | });
37 | }
38 |
--------------------------------------------------------------------------------
/alchemy/src/fs/static-vue-file.ts:
--------------------------------------------------------------------------------
1 | import { File } from "./file.ts";
2 |
3 | export type StaticVueFile = File;
4 |
5 | /**
6 | * Creates a static Vue component file
7 | *
8 | * @example
9 | * // Create a Vue component file with content
10 | * const button = await StaticVueFile("Button.vue",
11 | * `
12 | *
13 | *
14 | *
15 | *
22 | *
23 | * `
28 | * );
29 | */
30 | export function StaticVueFile(
31 | id: string,
32 | ...args: [content: string] | [path: string, content: string]
33 | ): Promise {
34 | const [path, content] = args.length === 1 ? [id, args[0]] : args;
35 | return File(id, {
36 | path,
37 | content,
38 | });
39 | }
40 |
--------------------------------------------------------------------------------
/alchemy/src/fs/static-yaml-file.ts:
--------------------------------------------------------------------------------
1 | import { File } from "./file.ts";
2 | /**
3 | * Creates a YAML file with formatted content
4 | *
5 | * @example
6 | * // Create a YAML configuration file
7 | * const config = await StaticYamlFile("config.yaml", {
8 | * server: {
9 | * host: "localhost",
10 | * port: 3000
11 | * },
12 | * database: {
13 | * url: "postgresql://localhost:5432/db",
14 | * pool: {
15 | * min: 1,
16 | * max: 10
17 | * }
18 | * }
19 | * });
20 | */
21 | export type StaticYamlFile = File;
22 |
23 | export async function StaticYamlFile(
24 | id: string,
25 | ...args: [content: any] | [path: string, content: any]
26 | ): Promise {
27 | const [path, content] = args.length === 1 ? [id, args[0]] : args;
28 | const yaml = await import("yaml");
29 | return File(id, {
30 | path,
31 | content: yaml.stringify(content),
32 | });
33 | }
34 |
--------------------------------------------------------------------------------
/alchemy/src/github/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./repository-environment.ts";
2 | export * from "./secret.ts";
3 |
--------------------------------------------------------------------------------
/alchemy/src/index.ts:
--------------------------------------------------------------------------------
1 | export type { AlchemyOptions } from "./alchemy.ts";
2 | export type * from "./context.ts";
3 | export * from "./resource.ts";
4 | export type * from "./scope.ts";
5 | export * from "./secret.ts";
6 | export * from "./serde.ts";
7 | export * from "./state.ts";
8 | export * from "./type.ts";
9 | export * from "./util/ignore.ts";
10 |
11 | import { alchemy } from "./alchemy.ts";
12 | export default alchemy;
13 |
--------------------------------------------------------------------------------
/alchemy/src/neon/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./api-error.ts";
2 | export * from "./api.ts";
3 | export * from "./project.ts";
4 |
--------------------------------------------------------------------------------
/alchemy/src/os/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./exec.ts";
2 |
--------------------------------------------------------------------------------
/alchemy/src/runtime/global.ts:
--------------------------------------------------------------------------------
1 | import type { SerializedScope } from "../serde.ts";
2 |
3 | declare global {
4 | const __ALCHEMY_WORKER_NAME__: string;
5 | const __ALCHEMY_RUNTIME__: true;
6 | const __ALCHEMY_SERIALIZED_SCOPE__: SerializedScope;
7 | }
8 |
9 | // __ALCHEMY_SERIALIZED_SCOPE__ is injected by esbuild when bundling a Worker
10 | export const isRuntime = typeof __ALCHEMY_RUNTIME__ !== "undefined";
11 |
--------------------------------------------------------------------------------
/alchemy/src/runtime/shims.js:
--------------------------------------------------------------------------------
1 | import { env } from "cloudflare:workers";
2 | import { Scope } from "../scope.js";
3 | import { Secret } from "../secret.js";
4 | import { deserialize } from "../serde.js";
5 |
6 | globalThis.process.env = env;
7 |
8 | export const __ALCHEMY_RUNTIME__ = true;
9 |
10 | export var __ALCHEMY_SERIALIZED_SCOPE__ = JSON.parse(
11 | env.__ALCHEMY_SERIALIZED_SCOPE__,
12 | );
13 |
14 | export const STATE = {
15 | async get(id) {
16 | const fqn = globalThis.__ALCHEMY_SCOPE__.current.fqn(id);
17 | const state = __ALCHEMY_SERIALIZED_SCOPE__[fqn];
18 | if (!state) {
19 | throw new Error(
20 | `Resource ${fqn} not found in __ALCHEMY_SERIALIZED_SCOPE__\n${JSON.stringify(__ALCHEMY_SERIALIZED_SCOPE__, null, 2)}`,
21 | );
22 | }
23 | return await deserialize(Scope.current, state, {
24 | transform: (value) => {
25 | if (value && typeof value === "object" && value["@secret-env"]) {
26 | return {
27 | value: new Secret(env[value["@secret-env"]]),
28 | };
29 | }
30 | },
31 | });
32 | },
33 | };
34 | // export const process = { env };
35 |
--------------------------------------------------------------------------------
/alchemy/src/runtime/state.ts:
--------------------------------------------------------------------------------
1 | import type { Resource } from "../resource.ts";
2 |
3 | export interface RuntimeState {
4 | get(id: string): Resource;
5 | }
6 |
7 | // /**
8 | // * Runtime state injected into the environment.
9 | // */
10 |
--------------------------------------------------------------------------------
/alchemy/src/sentry/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./api.ts";
2 | export * from "./client-key.ts";
3 | export * from "./project.ts";
4 | export * from "./team.ts";
5 |
--------------------------------------------------------------------------------
/alchemy/src/stripe/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./meter.ts";
2 | export * from "./price.ts";
3 | export * from "./product.ts";
4 | export * from "./webhook.ts";
5 |
--------------------------------------------------------------------------------
/alchemy/src/type.ts:
--------------------------------------------------------------------------------
1 | export type type = typeof type;
2 | /**
3 | * Used to construct type-level alias information.
4 | */
5 | export const type = ((): any => {
6 | throw new Error("Should never be called, purely for type-level aliasing");
7 | }) as (() => T) &
8 | // we also want to make this a "class type" so that syntax highlighting is always as a type
9 | (new () => T);
10 |
--------------------------------------------------------------------------------
/alchemy/src/upstash/error.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Custom error class for Upstash API errors
3 | */
4 | export class UpstashError extends Error {
5 | /**
6 | * HTTP status code
7 | */
8 | statusCode: number;
9 |
10 | /**
11 | * Original response object
12 | */
13 | response: Response;
14 |
15 | /**
16 | * Create a new Upstash error
17 | *
18 | * @param message Error message
19 | * @param statusCode HTTP status code
20 | * @param response Original response object
21 | */
22 | constructor(message: string, statusCode: number, response: Response) {
23 | super(message);
24 | this.name = "UpstashError";
25 | this.statusCode = statusCode;
26 | this.response = response;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/alchemy/src/upstash/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./api.ts";
2 | export * from "./error.ts";
3 | export * from "./redis.ts";
4 |
--------------------------------------------------------------------------------
/alchemy/src/util/assert-never.ts:
--------------------------------------------------------------------------------
1 | export function assertNever(value: never): never {
2 | throw new Error(`Unexpected value: ${value}`);
3 | }
4 |
--------------------------------------------------------------------------------
/alchemy/src/util/content-type.ts:
--------------------------------------------------------------------------------
1 | import path from "node:path";
2 |
3 | // Common MIME types
4 | const mimeTypes: Record = {
5 | ".html": "text/html",
6 | ".htm": "text/html",
7 | ".css": "text/css",
8 | ".js": "application/javascript",
9 | ".mjs": "application/javascript+module",
10 | ".json": "application/json",
11 | ".xml": "application/xml",
12 | ".txt": "text/plain",
13 | ".md": "text/markdown",
14 | ".png": "image/png",
15 | ".jpg": "image/jpeg",
16 | ".jpeg": "image/jpeg",
17 | ".gif": "image/gif",
18 | ".webp": "image/webp",
19 | ".svg": "image/svg+xml",
20 | ".ico": "image/x-icon",
21 | ".pdf": "application/pdf",
22 | ".zip": "application/zip",
23 | ".woff": "font/woff",
24 | ".woff2": "font/woff2",
25 | ".ttf": "font/ttf",
26 | ".otf": "font/otf",
27 | ".eot": "application/vnd.ms-fontobject",
28 | ".mp4": "video/mp4",
29 | ".webm": "video/webm",
30 | ".mp3": "audio/mpeg",
31 | ".wav": "audio/wav",
32 | ".wasm": "application/wasm",
33 | };
34 |
35 | /**
36 | * Gets the content type for a file based on its extension
37 | *
38 | * @param filePath Path to the file
39 | * @returns The content type for the file
40 | */
41 | export function getContentType(filePath: string): string | undefined {
42 | return mimeTypes[path.extname(filePath).toLowerCase()];
43 | }
44 |
--------------------------------------------------------------------------------
/alchemy/src/util/ignore.ts:
--------------------------------------------------------------------------------
1 | export async function ignore(
2 | codes: string | string[],
3 | fn: () => Promise,
4 | ): Promise {
5 | try {
6 | return await fn();
7 | } catch (error: any) {
8 | const errorCode = error.code || error.name;
9 | if (
10 | Array.isArray(codes) ? codes.includes(errorCode) : errorCode === codes
11 | ) {
12 | return undefined;
13 | }
14 | throw error;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/alchemy/src/util/retry.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Utility function for exponential backoff retry
3 | * Retries an operation with exponential backoff when a retryable error occurs
4 | *
5 | * @param operation The async operation to execute and potentially retry
6 | * @param isRetryable Function to determine if an error should trigger a retry
7 | * @param maxAttempts Maximum number of attempts before giving up
8 | * @param initialDelayMs Initial delay in milliseconds before first retry
9 | * @returns Result of the operation
10 | * @throws The last error encountered if all retries fail
11 | */
12 | export async function withExponentialBackoff(
13 | operation: () => Promise,
14 | isRetryable: (error: any) => boolean,
15 | maxAttempts = 5,
16 | initialDelayMs = 100,
17 | ): Promise {
18 | let attempt = 0;
19 | let delay = initialDelayMs;
20 |
21 | while (true) {
22 | try {
23 | return await operation();
24 | } catch (error: any) {
25 | console.log(error.message);
26 | attempt++;
27 | if (attempt >= maxAttempts || !isRetryable(error)) {
28 | throw error;
29 | }
30 |
31 | // Exponential backoff with jitter
32 | const jitter = Math.random() * 0.1 * delay;
33 | await new Promise((resolve) => setTimeout(resolve, delay + jitter));
34 | delay *= 2; // Double the delay for next attempt
35 | delay = Math.min(delay, 10000); // Cap at 10 seconds
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/alchemy/src/util/rm.ts:
--------------------------------------------------------------------------------
1 | import fs from "node:fs/promises";
2 |
3 | export async function rm(path: string) {
4 | try {
5 | await fs.rm(path, { recursive: true, force: true });
6 | } catch (error: any) {
7 | if (error.code !== "ENOENT") {
8 | throw error;
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/alchemy/src/util/sha256.ts:
--------------------------------------------------------------------------------
1 | import { createHash } from "node:crypto";
2 |
3 | export function sha256(value: string): string {
4 | return createHash("sha256").update(value).digest("hex");
5 | }
6 |
--------------------------------------------------------------------------------
/alchemy/src/util/sleep.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Pauses execution for a specified number of milliseconds.
3 | * @param ms The number of milliseconds to sleep.
4 | * @returns A promise that resolves after the specified time.
5 | */
6 | export function sleep(ms: number): Promise {
7 | return new Promise((resolve) => setTimeout(resolve, ms));
8 | }
9 |
--------------------------------------------------------------------------------
/alchemy/src/util/slugify.ts:
--------------------------------------------------------------------------------
1 | export function slugify(str: string, delimiter = "-") {
2 | return str.toLowerCase().replace(/[^a-z0-9]/gi, delimiter);
3 | }
4 |
--------------------------------------------------------------------------------
/alchemy/src/vercel/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./api.ts";
2 | export * from "./project-domain.ts";
3 | export * from "./project.ts";
4 |
--------------------------------------------------------------------------------
/alchemy/src/web/vitepress/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./config.ts";
2 | export * from "./custom-theme.ts";
3 | export * from "./home-page.ts";
4 | export * from "./process-front-matter-files.ts";
5 | export * from "./vitepress.ts";
6 |
--------------------------------------------------------------------------------
/alchemy/test/alchemy.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect } from "vitest";
2 | import { alchemy } from "../src/alchemy.ts";
3 | import { BRANCH_PREFIX } from "./util.ts";
4 |
5 | import "../src/test/vitest.ts";
6 |
7 | const test = alchemy.test(import.meta, {
8 | prefix: BRANCH_PREFIX,
9 | });
10 |
11 | describe("alchemy.run", async () => {
12 | describe("read mode", async () => {
13 | test("can create a scope", async (scope) => {
14 | expect(scope.phase).toBe("up");
15 |
16 | await alchemy.run("child", { phase: "read" }, async (child) => {
17 | expect(child.phase).toBe("read");
18 | expect(child.appName).toBeUndefined();
19 | expect(child.scopeName).toBe("child");
20 | expect(child.parent).toBe(scope);
21 | });
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/alchemy/test/aws/control/test-utils.ts:
--------------------------------------------------------------------------------
1 | import { createCloudControlClient } from "../../../src/aws/control/client.js";
2 |
3 | const client = await createCloudControlClient();
4 |
5 | /**
6 | * Wait for a resource to be stably deleted (not just gone once due to eventual consistency)
7 | */
8 | export async function waitForStableDeletion(
9 | typeName: string,
10 | id: string,
11 | {
12 | maxWaitMs = 60_000, // give up after a minute
13 | stablePeriodMs = 5_000, // require this long with no re-appearance
14 | pollBaseDelayMs = 500, // first poll delay
15 | pollMaxDelayMs = 5_000, // max back-off
16 | } = {},
17 | ) {
18 | let stableFor = 0;
19 | let delay = pollBaseDelayMs;
20 | const started = Date.now();
21 |
22 | while (Date.now() - started < maxWaitMs) {
23 | const res = await client.getResource(typeName, id);
24 |
25 | if (res === undefined) {
26 | // resource is currently gone – keep watching until it has *stayed* gone
27 | await new Promise((r) => setTimeout(r, delay));
28 | stableFor += delay;
29 | if (stableFor >= stablePeriodMs) return; // really gone
30 | } else {
31 | // it re-appeared – reset the stability timer
32 | stableFor = 0;
33 | await new Promise((r) => setTimeout(r, delay));
34 | }
35 |
36 | delay = Math.min(delay * 2, pollMaxDelayMs); // exponential back-off
37 | }
38 |
39 | throw new Error(
40 | `Resource ${typeName} ${id} still exists after ${maxWaitMs} ms`,
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/alchemy/test/cloudflare/account-id.test.ts:
--------------------------------------------------------------------------------
1 | import { beforeAll, describe, expect } from "vitest";
2 | import { alchemy } from "../../src/alchemy.ts";
3 | import { AccountId } from "../../src/cloudflare/account-id.ts";
4 | import { createCloudflareApi } from "../../src/cloudflare/api.ts";
5 | import { BRANCH_PREFIX } from "../util.ts";
6 |
7 | import { destroy } from "../../src/destroy.ts";
8 | import "../../src/test/vitest.ts";
9 |
10 | const test = alchemy.test(import.meta, {
11 | prefix: BRANCH_PREFIX,
12 | });
13 |
14 | describe("Cloudflare Account ID", () => {
15 | let expectedAccountId: string;
16 |
17 | // Get the expected account ID before running tests
18 | beforeAll(async () => {
19 | const api = await createCloudflareApi();
20 | expectedAccountId = api.accountId;
21 | });
22 |
23 | test("AccountId function", async (scope) => {
24 | try {
25 | // Get account ID with default options
26 | const accountId = await AccountId();
27 |
28 | // Verify account ID was retrieved correctly
29 | expect(accountId).toBeTruthy();
30 | expect(accountId).toEqual(expectedAccountId);
31 |
32 | // Verify account ID can be retrieved with explicit options
33 | const accountIdWithOptions = await AccountId({
34 | accountId: expectedAccountId,
35 | });
36 |
37 | expect(accountIdWithOptions).toEqual(expectedAccountId);
38 | } finally {
39 | await destroy(scope);
40 | }
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/alchemy/test/cloudflare/browser-handler.ts:
--------------------------------------------------------------------------------
1 | import puppeteer from "@cloudflare/puppeteer";
2 |
3 | export default {
4 | async fetch(request: Request, env: any) {
5 | const { searchParams } = new URL(request.url);
6 | let url = searchParams.get("url");
7 | let img;
8 |
9 | if (url) {
10 | url = new URL(url).toString(); // normalize
11 | img = await env.BROWSER_KV_DEMO.get(url, { type: "arrayBuffer" });
12 |
13 | if (img === null) {
14 | const browser = await puppeteer.launch(env.MYBROWSER);
15 | const page = await browser.newPage();
16 | await page.goto(url);
17 | img = await page.screenshot();
18 |
19 | await env.BROWSER_KV_DEMO.put(url, img, {
20 | expirationTtl: 60 * 60 * 24,
21 | });
22 |
23 | await browser.close();
24 | }
25 |
26 | return new Response(img, {
27 | headers: {
28 | "content-type": "image/jpeg",
29 | },
30 | });
31 | } else {
32 | return new Response("Please add an ?url=https://example.com/ parameter");
33 | }
34 | },
35 | };
36 |
--------------------------------------------------------------------------------
/alchemy/test/cloudflare/bundle-handler-als.ts:
--------------------------------------------------------------------------------
1 | import hooks from "node:async_hooks";
2 |
3 | export default {
4 | async fetch(): Promise {
5 | return new Response(typeof hooks.AsyncLocalStorage);
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/alchemy/test/cloudflare/bundle-handler.ts:
--------------------------------------------------------------------------------
1 | import { initLogger } from "braintrust";
2 | // biome-ignore lint/style/useNodejsImportProtocol: we are testing `crypto` and `node:crypto`
3 | import crypto from "crypto";
4 | import crypto2 from "node:crypto";
5 |
6 | export default {
7 | async fetch(_request: Request, env: any): Promise {
8 | const logger = initLogger({
9 | projectName: "My Project",
10 | apiKey: env.BRAINTRUST_API_KEY,
11 | asyncFlush: false,
12 | });
13 | console.log(crypto.randomBytes(10));
14 | console.log(crypto2.randomBytes(10));
15 | console.log(logger);
16 | require("ws");
17 | return new Response("Hello World!");
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/alchemy/test/cloudflare/migrations/001_create_table.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE test_migrations_table (
2 | id INTEGER PRIMARY KEY,
3 | name TEXT
4 | );
5 |
--------------------------------------------------------------------------------
/alchemy/test/cloudflare/nobundle/dir/bar.js:
--------------------------------------------------------------------------------
1 | export const bar = "bar";
--------------------------------------------------------------------------------
/alchemy/test/cloudflare/nobundle/foo.js:
--------------------------------------------------------------------------------
1 | export const foo = "foo";
--------------------------------------------------------------------------------
/alchemy/test/cloudflare/nobundle/index.js:
--------------------------------------------------------------------------------
1 | import { bar } from "./dir/bar.js";
2 | import { foo } from "./foo.js";
3 |
4 | export default {
5 | async fetch() {
6 | return new Response(JSON.stringify({
7 | foo,
8 | bar
9 | }))
10 | }
11 | };
--------------------------------------------------------------------------------
/alchemy/test/cloudflare/r2-rest-state-store.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect } from "vitest";
2 | import { alchemy } from "../../src/alchemy.ts";
3 | import { createCloudflareApi } from "../../src/cloudflare/api.ts";
4 | import { getBucket } from "../../src/cloudflare/bucket.ts";
5 | import { BRANCH_PREFIX } from "../util.ts";
6 |
7 | import { R2RestStateStore } from "../../src/cloudflare/r2-rest-state-store.ts";
8 | import "../../src/test/vitest.ts";
9 |
10 | describe("R2RestStateStore", async () => {
11 | const test = alchemy.test(import.meta, {
12 | // Isolate the default state store bucket from other tests' stores
13 | prefix: `${BRANCH_PREFIX}-r2-rest-state-store`,
14 | stateStore: (scope) => new R2RestStateStore(scope),
15 | });
16 |
17 | // For public access, we still need to use the Cloudflare API
18 | // This is one feature not available through the S3 API
19 | const api = await createCloudflareApi();
20 |
21 | test("optimistically creates alchemy-state bucket", async () => {
22 | const defaultBucketName = "alchemy-state";
23 | const bucket = await getBucket(api, defaultBucketName);
24 |
25 | expect(bucket.result.name).toEqual(defaultBucketName);
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/alchemy/test/cloudflare/unenv-handler.ts:
--------------------------------------------------------------------------------
1 | import fs from "node:fs/promises";
2 |
3 | export default {
4 | async fetch(
5 | _request: Request,
6 | _env: any,
7 | _ctx: ExecutionContext,
8 | ): Promise {
9 | return new Response(typeof fs.readFile);
10 | },
11 | };
12 |
--------------------------------------------------------------------------------
/alchemy/test/cloudflare/unenv.test.ts:
--------------------------------------------------------------------------------
1 | import * as path from "node:path";
2 | import { describe, expect } from "vitest";
3 | import { alchemy } from "../../src/alchemy.ts";
4 | import { Worker } from "../../src/cloudflare/worker.ts";
5 | import { destroy } from "../../src/destroy.ts";
6 | import { BRANCH_PREFIX } from "../util.ts";
7 |
8 | import "@cloudflare/unenv-preset/node/process";
9 |
10 | import "../../src/test/vitest.ts";
11 | import { fetchAndExpectOK } from "./fetch-utils.ts";
12 |
13 | const test = alchemy.test(import.meta, {
14 | prefix: BRANCH_PREFIX,
15 | });
16 |
17 | describe("Worker Unenv Tests", () => {
18 | test("create worker with import.meta.dirname and unenv-handler", async (scope) => {
19 | try {
20 | // Create a temporary directory for the files
21 | // Create the worker using the entrypoint file
22 | const worker = await Worker(`${BRANCH_PREFIX}-test-worker-unenv`, {
23 | entrypoint: path.join(import.meta.dirname, "unenv-handler.ts"),
24 | format: "esm",
25 | url: true, // Enable workers.dev URL to test the worker
26 | compatibilityFlags: ["nodejs_compat"],
27 | });
28 |
29 | const response = await fetchAndExpectOK(worker.url!);
30 | expect(await response.text()).toEqual("function");
31 | } finally {
32 | // Clean up the worker
33 | await destroy(scope);
34 | }
35 | }, 120000); // Increased timeout for bundling operations
36 | });
37 |
--------------------------------------------------------------------------------
/alchemy/test/cloudflare/version-metadata-handler.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | async fetch(_request: Request, env: any): Promise {
3 | if (env.IMAGES && env.IMAGES.type === "images") {
4 | return new Response("Images binding available", { status: 200 });
5 | }
6 | if (env.VERSION_METADATA) {
7 | return new Response("VersionMetadata binding available", { status: 200 });
8 | }
9 | return new Response("Binding not found", { status: 500 });
10 | },
11 | };
12 |
--------------------------------------------------------------------------------
/alchemy/test/cloudflare/version-metadata.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect } from "vitest";
2 | import { alchemy } from "../../src/alchemy.ts";
3 | import { VersionMetadata } from "../../src/cloudflare/version-metadata.ts";
4 | import { Worker } from "../../src/cloudflare/worker.ts";
5 | import { destroy } from "../../src/destroy.ts";
6 | import { BRANCH_PREFIX } from "../util.ts";
7 |
8 | import path from "node:path";
9 | import "../../src/test/vitest.ts";
10 | import { fetchAndExpectOK } from "./fetch-utils.ts";
11 |
12 | const test = alchemy.test(import.meta, {
13 | prefix: BRANCH_PREFIX,
14 | });
15 |
16 | describe("VersionMetadata Binding", () => {
17 | test("create worker with version metadata binding", async (scope) => {
18 | const workerName = `${BRANCH_PREFIX}-version-metadata-worker`;
19 |
20 | let worker: Worker | undefined;
21 |
22 | try {
23 | worker = await Worker(workerName, {
24 | name: workerName,
25 | entrypoint: path.join(
26 | import.meta.dirname,
27 | "version-metadata-handler.ts",
28 | ),
29 | format: "esm",
30 | url: true,
31 | bindings: {
32 | VERSION_METADATA: new VersionMetadata(),
33 | },
34 | });
35 |
36 | expect(worker.id).toBeTruthy();
37 | expect(worker.name).toEqual(workerName);
38 | expect(worker.bindings).toBeDefined();
39 | expect(worker.url).toBeTruthy();
40 |
41 | const response = await fetchAndExpectOK(worker.url!);
42 | const text = await response.text();
43 | expect(text).toContain("VersionMetadata binding available");
44 | } finally {
45 | await destroy(scope);
46 | }
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/alchemy/test/esbuild.test.ts:
--------------------------------------------------------------------------------
1 | import fs from "node:fs/promises";
2 | import path from "node:path";
3 | import { afterAll, expect } from "vitest";
4 | import { alchemy } from "../src/alchemy.ts";
5 | import { Bundle } from "../src/esbuild/bundle.ts";
6 | import { BRANCH_PREFIX, exists } from "./util.ts";
7 |
8 | import { destroy } from "../src/destroy.ts";
9 | import "../src/test/vitest.ts";
10 |
11 | const test = alchemy.test(import.meta, {
12 | prefix: BRANCH_PREFIX,
13 | });
14 |
15 | const out = path.join(".alchemy", ".out");
16 | const outputFile = path.join(out, "handler.js");
17 |
18 | afterAll(async () => {
19 | await fs.rmdir(out);
20 | });
21 |
22 | test("bundle and cleanup", async (scope) => {
23 | const bundle = await Bundle("bundle", {
24 | entryPoint: path.join(import.meta.dirname, "handler.ts"),
25 | outdir: out,
26 | format: "esm",
27 | platform: "node",
28 | target: "node18",
29 | });
30 |
31 | try {
32 | // Apply the bundle
33 | expect(bundle.path).toBe(outputFile);
34 | expect(bundle.hash).toBeTruthy();
35 |
36 | // Verify the file exists and contains our code
37 | expect(await exists(outputFile)).toBe(true);
38 | const contents = await fs.readFile(outputFile, "utf-8");
39 | expect(contents).toContain("Hello from bundled handler");
40 | } finally {
41 | await destroy(scope);
42 | expect(await exists(outputFile)).toBe(false);
43 | }
44 | });
45 |
--------------------------------------------------------------------------------
/alchemy/test/handler.ts:
--------------------------------------------------------------------------------
1 | export async function handler(event: any) {
2 | console.log("Received event:", JSON.stringify(event));
3 |
4 | // For Lambda URL, the actual payload is in the body
5 | const payload = event.body
6 | ? typeof event.body === "string"
7 | ? JSON.parse(event.body)
8 | : event.body
9 | : event;
10 |
11 | return {
12 | statusCode: 200,
13 | body: JSON.stringify({
14 | message: "Hello from bundled handler!",
15 | event: payload,
16 | }),
17 | };
18 | }
19 | // test case for handlers with _, 0-9, and A-Z
20 | export const _myHandler012 = handler;
21 |
--------------------------------------------------------------------------------
/alchemy/test/run.ts:
--------------------------------------------------------------------------------
1 | import { runChangedTests } from "../src/test/prune.js";
2 |
3 | /**
4 | * This script detects which tests have changed using esbuild and git and then runs only those tests.
5 | */
6 |
7 | const sinceIdx = process.argv.findIndex((arg) => arg === "--since");
8 | const since =
9 | (sinceIdx !== -1 ? process.argv[sinceIdx + 1] : undefined) ?? "HEAD~1";
10 |
11 | const vitestIdx = process.argv.findIndex((arg) => arg === "--vitest");
12 | const useVitest = vitestIdx !== -1;
13 |
14 | await runChangedTests(import.meta.dirname, since, useVitest);
15 |
--------------------------------------------------------------------------------
/alchemy/test/util.ts:
--------------------------------------------------------------------------------
1 | import fs from "node:fs/promises";
2 | import os from "node:os";
3 |
4 | /**
5 | * Check if a file or directory exists
6 | * Uses fs.access which is available in all Node.js versions
7 | */
8 | export async function exists(path: string): Promise {
9 | try {
10 | await fs.access(path);
11 | return true;
12 | } catch {
13 | return false;
14 | }
15 | }
16 |
17 | /**
18 | * Sanitize a string to be safe for AWS resource names
19 | * Replaces any characters that aren't alphanumeric, hyphen, or underscore
20 | */
21 | function sanitizeForAwsResourceName(str: string): string {
22 | // Replace any character that's not alphanumeric, hyphen, or underscore with a hyphen
23 | return str.replace(/[^a-zA-Z0-9\-_]/g, "-");
24 | }
25 |
26 | /**
27 | * Branch prefix for resource names to avoid naming conflicts in CI/CD
28 | *
29 | * Uses BRANCH_PREFIX environment variable in CI/CD environments
30 | * Falls back to current user's name in local development
31 | * Sanitizes to ensure only valid characters for AWS resource names
32 | */
33 | export const BRANCH_PREFIX = sanitizeForAwsResourceName(
34 | process.env.BRANCH_PREFIX || os.userInfo().username,
35 | );
36 |
--------------------------------------------------------------------------------
/alchemy/test/util/dedent.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from "vitest";
2 | import { dedent } from "../../src/util/dedent.ts";
3 |
4 | describe("dedent", () => {
5 | it("removes common indentation", () => {
6 | const s = dedent`
7 | line1
8 | line2
9 | line3
10 | `;
11 | expect(s).toBe("line1\n line2\nline3");
12 | });
13 |
14 | it("removes leading and trailing blank lines", () => {
15 | const s = dedent`
16 |
17 | line1
18 | line2
19 |
20 | `;
21 | expect(s).toBe("line1\nline2");
22 | });
23 |
24 | it("preserves extra indentation", () => {
25 | const s = dedent`
26 | level1
27 | level2
28 | level3
29 | `;
30 | expect(s).toBe("level1\n level2\n level3");
31 | });
32 |
33 | it("handles empty template", () => {
34 | const s = dedent``;
35 | expect(s).toBe("");
36 | });
37 |
38 | it("handles single line", () => {
39 | const s = dedent`single line`;
40 | expect(s).toBe("single line");
41 | });
42 |
43 | it("interpolates values correctly", () => {
44 | const value = "world";
45 | const s = dedent`
46 | hello ${value}
47 | bye
48 | `;
49 | expect(s).toBe("hello world\nbye");
50 | });
51 |
52 | it("preserves blank lines in between", () => {
53 | const s = dedent`
54 | line1
55 |
56 | line3
57 | `;
58 | expect(s).toBe("line1\n\nline3");
59 | });
60 |
61 | it("returns empty for only whitespace lines", () => {
62 | const s = dedent`
63 |
64 |
65 | `;
66 | expect(s).toBe("");
67 | });
68 | });
69 |
--------------------------------------------------------------------------------
/alchemy/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.base.json",
3 | "include": ["src/**/*.ts", "src/**/*.js"],
4 | "compilerOptions": {
5 | "composite": true,
6 | "noEmit": false,
7 | "outDir": "./lib",
8 | "rootDir": "./src",
9 | "sourceMap": true,
10 | "declarationMap": true,
11 | "module": "Preserve",
12 | "moduleResolution": "Bundler",
13 | "target": "ESNext",
14 | "allowJs": true,
15 | "allowImportingTsExtensions": true,
16 | "rewriteRelativeImportExtensions": true
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/alchemy/tsconfig.test.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": ["src/**/*.ts", "test/**/*.ts", "src/runtime/shims.js"],
4 | "compilerOptions": {
5 | "rootDir": ".",
6 | "noEmit": true,
7 | "types": ["bun-types", "@cloudflare/workers-types"]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/examples/aws-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aws-app",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "bun run index.ts",
7 | "deploy": "bun run --env-file ../../.env ./alchemy.run.ts",
8 | "destroy": "bun run --env-file ../../.env ./alchemy.run.ts --destroy"
9 | },
10 | "dependencies": {
11 | "alchemy": "workspace:*"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/aws-app/src/index.ts:
--------------------------------------------------------------------------------
1 | export function handler(_event: any, _context: any) {
2 | console.log("Hello, World!");
3 | }
4 |
--------------------------------------------------------------------------------
/examples/aws-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["src/**/*.ts", "alchemy.run.ts"],
4 | "compilerOptions": {
5 | "composite": true,
6 | "resolveJsonModule": true
7 | },
8 | "references": [{ "path": "../../alchemy/tsconfig.json" }]
9 | }
10 |
--------------------------------------------------------------------------------
/examples/cloudflare-nuxt-pipeline/.gitignore:
--------------------------------------------------------------------------------
1 | # Nuxt dev/build outputs
2 | .output
3 | .data
4 | .nuxt
5 | .nitro
6 | .cache
7 | dist
8 |
9 | # Node dependencies
10 | node_modules
11 |
12 | # Logs
13 | logs
14 | *.log
15 |
16 | # Misc
17 | .DS_Store
18 | .fleet
19 | .idea
20 |
21 | # Local env files
22 | .env
23 | .env.*
24 | !.env.example
25 | wrangler.jsonc
26 |
--------------------------------------------------------------------------------
/examples/cloudflare-nuxt-pipeline/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/examples/cloudflare-nuxt-pipeline/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import type { worker } from "./alchemy.run.js";
3 |
4 | export type WorkerEnv = typeof worker.Env;
5 |
6 | declare module "cloudflare:workers" {
7 | namespace Cloudflare {
8 | export interface Env extends WorkerEnv {}
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/examples/cloudflare-nuxt-pipeline/index.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import type { WorkerEnv } from "./env.ts";
3 | // @ts-ignore - Suppress type errors if the module isn't found during editing/linting
4 | import nitroApp from "./.output/server/index.mjs";
5 |
6 | export default {
7 | async fetch(
8 | request: Request,
9 | environment: WorkerEnv,
10 | ctx: ExecutionContext,
11 | ): Promise {
12 | const url = new URL(request.url);
13 |
14 | if (url.pathname.startsWith("/api/")) {
15 | return nitroApp.fetch(request, environment, ctx);
16 | }
17 |
18 | return environment.ASSETS.fetch(request);
19 | },
20 | };
21 |
--------------------------------------------------------------------------------
/examples/cloudflare-nuxt-pipeline/nuxt.config.ts:
--------------------------------------------------------------------------------
1 | // https://nuxt.com/docs/api/configuration/nuxt-config
2 | export default defineNuxtConfig({
3 | compatibilityDate: "2025-04-21",
4 | devtools: { enabled: true },
5 | nitro: {
6 | preset: "cloudflare-module",
7 | prerender: {
8 | routes: ["/"],
9 | autoSubfolderIndex: false,
10 | },
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/examples/cloudflare-nuxt-pipeline/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nuxt-pipeline",
3 | "private": true,
4 | "type": "module",
5 | "scripts": {
6 | "build": "nuxt build",
7 | "dev": "nuxt dev",
8 | "generate": "nuxt generate",
9 | "preview": "nuxt preview",
10 | "postinstall": "nuxt prepare",
11 | "deploy": "bun run --env-file ../../.env ./alchemy.run.ts",
12 | "destroy": "bun run --env-file ../../.env ./alchemy.run.ts --destroy"
13 | },
14 | "dependencies": {
15 | "@cloudflare/workers-types": "^4.20250421.0",
16 | "alchemy": "workspace:*",
17 | "cloudflare": "^4.2.0",
18 | "nuxt": "^3.16.2",
19 | "vue": "^3.5.13",
20 | "vue-router": "^4.5.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/cloudflare-nuxt-pipeline/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-nuxt-pipeline/public/favicon.ico
--------------------------------------------------------------------------------
/examples/cloudflare-nuxt-pipeline/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-Agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/examples/cloudflare-nuxt-pipeline/server/api/pipeline.post.ts:
--------------------------------------------------------------------------------
1 | import { env } from "cloudflare:workers";
2 |
3 | export default defineEventHandler(async (event) => {
4 | try {
5 | const body = await readBody(event);
6 | // @ts-ignore
7 | const pipeline = env.PIPELINE;
8 | const data = body.data;
9 |
10 | if (!data) {
11 | throw new Error("Missing 'data' property in request body");
12 | }
13 |
14 | // Always send data wrapped in an array
15 | await pipeline.send([{ value: data }]);
16 |
17 | return { success: true, message: "Data sent to pipeline." };
18 | } catch (error) {
19 | console.error("Error sending data to pipeline:", error);
20 | throw createError({
21 | statusCode: 500,
22 | statusMessage: error instanceof Error ? error.message : "Pipeline error",
23 | });
24 | }
25 | });
26 |
--------------------------------------------------------------------------------
/examples/cloudflare-nuxt-pipeline/server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../.nuxt/tsconfig.server.json"
3 | }
4 |
--------------------------------------------------------------------------------
/examples/cloudflare-nuxt-pipeline/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // https://nuxt.com/docs/guide/concepts/typescript
3 | "extends": "./.nuxt/tsconfig.json",
4 | "compilerOptions": {
5 | "types": ["@cloudflare/workers-types", "@types/node"],
6 | "jsx": "react-jsx"
7 | },
8 | "references": [{ "path": "../../alchemy/tsconfig.json" }]
9 | }
10 |
--------------------------------------------------------------------------------
/examples/cloudflare-react-router/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /node_modules/
3 | *.tsbuildinfo
4 |
5 | # React Router
6 | /.react-router/
7 | /build/
8 |
9 | # Cloudflare
10 | .mf
11 | .wrangler
12 | .dev.vars*
13 |
14 | wrangler.json*
--------------------------------------------------------------------------------
/examples/cloudflare-react-router/alchemy.run.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import alchemy from "alchemy";
4 | import { R2RestStateStore, ReactRouter } from "alchemy/cloudflare";
5 |
6 | const BRANCH_PREFIX = process.env.BRANCH_PREFIX ?? "";
7 | const app = await alchemy("cloudflare-react-router", {
8 | stage: process.env.USER ?? "dev",
9 | phase: process.argv.includes("--destroy") ? "destroy" : "up",
10 | quiet: !process.argv.includes("--verbose"),
11 | password: process.env.ALCHEMY_PASSWORD,
12 | stateStore:
13 | process.env.ALCHEMY_STATE_STORE === "cloudflare"
14 | ? (scope) => new R2RestStateStore(scope)
15 | : undefined,
16 | });
17 |
18 | export const website = await ReactRouter(
19 | `cloudflare-react-router-website${BRANCH_PREFIX}`,
20 | {
21 | main: "./workers/app.ts",
22 | command: "bun run build",
23 | },
24 | );
25 |
26 | console.log({
27 | url: website.url,
28 | });
29 |
30 | await app.finalize();
31 |
--------------------------------------------------------------------------------
/examples/cloudflare-react-router/app/app.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss" source(".");
2 |
3 | @theme {
4 | --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif,
5 | "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
6 | }
7 |
8 | html,
9 | body {
10 | @apply bg-white dark:bg-gray-950;
11 |
12 | @media (prefers-color-scheme: dark) {
13 | color-scheme: dark;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/cloudflare-react-router/app/entry.server.tsx:
--------------------------------------------------------------------------------
1 | import type { AppLoadContext, EntryContext } from "react-router";
2 | import { ServerRouter } from "react-router";
3 | import { isbot } from "isbot";
4 | import { renderToReadableStream } from "react-dom/server";
5 |
6 | export default async function handleRequest(
7 | request: Request,
8 | responseStatusCode: number,
9 | responseHeaders: Headers,
10 | routerContext: EntryContext,
11 | _loadContext: AppLoadContext
12 | ) {
13 | let shellRendered = false;
14 | const userAgent = request.headers.get("user-agent");
15 |
16 | const body = await renderToReadableStream(
17 | ,
18 | {
19 | onError(error: unknown) {
20 | responseStatusCode = 500;
21 | // Log streaming rendering errors from inside the shell. Don't log
22 | // errors encountered during initial shell rendering since they'll
23 | // reject and get logged in handleDocumentRequest.
24 | if (shellRendered) {
25 | console.error(error);
26 | }
27 | },
28 | }
29 | );
30 | shellRendered = true;
31 |
32 | // Ensure requests from bots and SPA Mode renders wait for all content to load before responding
33 | // https://react.dev/reference/react-dom/server/renderToPipeableStream#waiting-for-all-content-to-load-for-crawlers-and-static-generation
34 | if ((userAgent && isbot(userAgent)) || routerContext.isSpaMode) {
35 | await body.allReady;
36 | }
37 |
38 | responseHeaders.set("Content-Type", "text/html");
39 | return new Response(body, {
40 | headers: responseHeaders,
41 | status: responseStatusCode,
42 | });
43 | }
44 |
--------------------------------------------------------------------------------
/examples/cloudflare-react-router/app/routes.ts:
--------------------------------------------------------------------------------
1 | import { type RouteConfig, index } from "@react-router/dev/routes";
2 |
3 | export default [index("routes/home.tsx")] satisfies RouteConfig;
4 |
--------------------------------------------------------------------------------
/examples/cloudflare-react-router/app/routes/home.tsx:
--------------------------------------------------------------------------------
1 | import type { Route } from "./+types/home";
2 | import { Welcome } from "../welcome/welcome";
3 |
4 | export function meta({}: Route.MetaArgs) {
5 | return [
6 | { title: "New React Router App" },
7 | { name: "description", content: "Welcome to React Router!" },
8 | ];
9 | }
10 |
11 | export function loader({ context }: Route.LoaderArgs) {
12 | return { message: context.cloudflare.env.VALUE_FROM_CLOUDFLARE };
13 | }
14 |
15 | export default function Home({ loaderData }: Route.ComponentProps) {
16 | return ;
17 | }
18 |
--------------------------------------------------------------------------------
/examples/cloudflare-react-router/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cloudflare-react-router",
3 | "private": true,
4 | "type": "module",
5 | "scripts": {
6 | "build": "react-router build",
7 | "deploy": "bun --env-file ../../.env ./alchemy.run.ts",
8 | "destroy": "bun --env-file ../../.env ./alchemy.run.ts --destroy",
9 | "dev": "react-router dev",
10 | "preview": "bun run build && vite preview",
11 | "types": "react-router typegen"
12 | },
13 | "dependencies": {
14 | "cloudflare": "^4.3.0",
15 | "isbot": "^5.1.27",
16 | "react": "^19.1.0",
17 | "react-dom": "^19.1.0",
18 | "react-router": "^7.5.3"
19 | },
20 | "devDependencies": {
21 | "@cloudflare/vite-plugin": "^1.0.12",
22 | "@cloudflare/workers-types": "^4.20250529.0",
23 | "@react-router/dev": "^7.5.3",
24 | "@tailwindcss/vite": "^4.1.4",
25 | "@types/node": "^20",
26 | "@types/react": "^19.1.2",
27 | "@types/react-dom": "^19.1.2",
28 | "tailwindcss": "^4.1.4",
29 | "typescript": "^5.8.3",
30 | "vite": "^6.3.3",
31 | "vite-tsconfig-paths": "^5.1.4",
32 | "wrangler": "^4.18.0"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/examples/cloudflare-react-router/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-react-router/public/favicon.ico
--------------------------------------------------------------------------------
/examples/cloudflare-react-router/react-router.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "@react-router/dev/config";
2 |
3 | export default {
4 | ssr: true,
5 | future: {
6 | unstable_viteEnvironmentApi: true,
7 | },
8 | } satisfies Config;
9 |
--------------------------------------------------------------------------------
/examples/cloudflare-react-router/tsconfig.cloudflare.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [
4 | "./alchemy.run.ts",
5 | ".react-router/types/**/*",
6 | "app/**/*",
7 | "app/**/.server/**/*",
8 | "app/**/.client/**/*",
9 | "workers/**/*",
10 | "types/**/*.ts"
11 | ],
12 | "compilerOptions": {
13 | "composite": true,
14 | "strict": true,
15 | "lib": ["DOM", "DOM.Iterable", "ES2022"],
16 | "types": ["vite/client", "@cloudflare/workers-types"],
17 | "target": "ES2022",
18 | "module": "ES2022",
19 | "moduleResolution": "bundler",
20 | "jsx": "react-jsx",
21 | "baseUrl": ".",
22 | "rootDirs": [".", "./.react-router/types"],
23 | "paths": {
24 | "~/*": ["./app/*"]
25 | },
26 | "esModuleInterop": true,
27 | "resolveJsonModule": true,
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/examples/cloudflare-react-router/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | { "path": "./tsconfig.node.json" },
5 | { "path": "./tsconfig.cloudflare.json" }
6 | ],
7 | "compilerOptions": {
8 | "checkJs": true,
9 | "verbatimModuleSyntax": true,
10 | "skipLibCheck": true,
11 | "strict": true,
12 | "noEmit": true,
13 | "types": ["./workers/env.ts", "@cloudflare/workers-types"]
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/cloudflare-react-router/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": ["vite.config.ts"],
4 | "compilerOptions": {
5 | "composite": true,
6 | "strict": true,
7 | "types": ["node"],
8 | "lib": ["ES2022"],
9 | "target": "ES2022",
10 | "module": "ES2022",
11 | "moduleResolution": "bundler"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/cloudflare-react-router/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { reactRouter } from "@react-router/dev/vite";
2 | import { cloudflare } from "@cloudflare/vite-plugin";
3 | import tailwindcss from "@tailwindcss/vite";
4 | import { defineConfig } from "vite";
5 | import tsconfigPaths from "vite-tsconfig-paths";
6 |
7 | export default defineConfig({
8 | plugins: [
9 | cloudflare({ viteEnvironment: { name: "ssr" } }),
10 | tailwindcss(),
11 | reactRouter(),
12 | tsconfigPaths(),
13 | ],
14 | });
15 |
--------------------------------------------------------------------------------
/examples/cloudflare-react-router/workers/app.ts:
--------------------------------------------------------------------------------
1 | import { createRequestHandler } from "react-router";
2 |
3 | declare module "react-router" {
4 | export interface AppLoadContext {
5 | cloudflare: {
6 | env: Env;
7 | ctx: ExecutionContext;
8 | };
9 | }
10 | }
11 |
12 | const requestHandler = createRequestHandler(
13 | () => import("virtual:react-router/server-build"),
14 | import.meta.env.MODE
15 | );
16 |
17 | export default {
18 | async fetch(request, env, ctx) {
19 | return requestHandler(request, {
20 | cloudflare: { env, ctx },
21 | });
22 | },
23 | } satisfies ExportedHandler;
24 |
--------------------------------------------------------------------------------
/examples/cloudflare-react-router/workers/env.ts:
--------------------------------------------------------------------------------
1 | import type { website } from "../alchemy.run.js";
2 |
3 | export type CloudflareEnv = typeof website.Env;
4 |
5 | declare global {
6 | type Env = CloudflareEnv
7 | }
8 |
9 | declare module "cloudflare:workers" {
10 | namespace Cloudflare {
11 | export interface Env extends CloudflareEnv {}
12 | }
13 | }
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/.env.example:
--------------------------------------------------------------------------------
1 | CLOUDFLARE_ACCOUNT_ID=__change_me__
2 | CLOUDFLARE_DATABASE_ID=__change_me__
3 | CLOUDFLARE_D1_TOKEN=__change_me__
4 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/.gitignore:
--------------------------------------------------------------------------------
1 | # Node modules
2 | node_modules
3 |
4 | # Logs
5 | logs
6 | *.log
7 | npm-debug.log*
8 | pnpm-debug.log*
9 |
10 | # Environment variables
11 | .env
12 | .dev.vars
13 |
14 | # Vite build output
15 | dist
16 |
17 | # TypeScript
18 | *.tsbuildinfo
19 |
20 | # IDEs and editors
21 | .vscode/
22 | .idea/
23 | *.suo
24 | *.ntvs*
25 | *.njsproj
26 | *.sln
27 | *.sw?
28 |
29 | # MacOS
30 | .DS_Store
31 |
32 | # Optional npm cache directory
33 | .npm
34 |
35 | # Optional eslint cache
36 | .eslintcache
37 |
38 | # Optional stylelint cache
39 | .stylelintcache
40 |
41 | # Optional REPL history
42 | .node_repl_history
43 |
44 | # Output of 'npm pack'
45 | *.tgz
46 |
47 | # pnpm store directory
48 | .pnpm-store
49 |
50 | # dotenv environment variables file
51 | .env.local
52 | .env.development.local
53 | .env.test.local
54 | .env.production.local
55 |
56 | # Vite cache
57 | .vite
58 |
59 | # Coverage directory used by tools like istanbul
60 | coverage
61 |
62 | # Temporary files
63 | *.tmp
64 | *.temp
65 |
66 | .wrangler
67 | wrangler.jsonc
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "overrides": [
3 | {
4 | "files": "*.jsonc",
5 | "options": {
6 | "trailingComma": "none"
7 | }
8 | }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/alchemy.run.ts:
--------------------------------------------------------------------------------
1 | import alchemy from "alchemy";
2 | import { D1Database, R2RestStateStore, Redwood } from "alchemy/cloudflare";
3 |
4 | const BRANCH_PREFIX = process.env.BRANCH_PREFIX ?? "";
5 |
6 | const app = await alchemy("cloudflare-redwood", {
7 | phase: process.argv.includes("--destroy") ? "destroy" : "up",
8 | stateStore:
9 | process.env.ALCHEMY_STATE_STORE === "cloudflare"
10 | ? (scope) => new R2RestStateStore(scope)
11 | : undefined,
12 | });
13 |
14 | const database = await D1Database(`cloudflare-redwood-db${BRANCH_PREFIX}`, {
15 | migrationsDir: "drizzle",
16 | });
17 |
18 | export const website = await Redwood(
19 | `cloudflare-redwood-website${BRANCH_PREFIX}`,
20 | {
21 | bindings: {
22 | DB: database,
23 | },
24 | },
25 | );
26 |
27 | console.log({
28 | url: website.url,
29 | });
30 |
31 | await app.finalize();
32 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/drizzle.config.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { defineConfig } from "drizzle-kit";
3 |
4 | export default defineConfig({
5 | out: "./drizzle",
6 | schema: "./src/db/schema.ts",
7 | dialect: "sqlite",
8 | driver: "d1-http",
9 | dbCredentials: {
10 | accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
11 | databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
12 | token: process.env.CLOUDFLARE_D1_TOKEN!,
13 | },
14 | });
15 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/drizzle/0000_lame_kitty_pryde.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE `users` (
2 | `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
3 | `name` text NOT NULL,
4 | `email` text NOT NULL
5 | );
6 | --> statement-breakpoint
7 | CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/drizzle/meta/0000_snapshot.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "6",
3 | "dialect": "sqlite",
4 | "id": "da6e7ea7-3118-455b-914c-14ff9239bd40",
5 | "prevId": "00000000-0000-0000-0000-000000000000",
6 | "tables": {
7 | "users": {
8 | "name": "users",
9 | "columns": {
10 | "id": {
11 | "name": "id",
12 | "type": "integer",
13 | "primaryKey": true,
14 | "notNull": true,
15 | "autoincrement": true
16 | },
17 | "name": {
18 | "name": "name",
19 | "type": "text",
20 | "primaryKey": false,
21 | "notNull": true,
22 | "autoincrement": false
23 | },
24 | "email": {
25 | "name": "email",
26 | "type": "text",
27 | "primaryKey": false,
28 | "notNull": true,
29 | "autoincrement": false
30 | }
31 | },
32 | "indexes": {
33 | "users_email_unique": {
34 | "name": "users_email_unique",
35 | "columns": ["email"],
36 | "isUnique": true
37 | }
38 | },
39 | "foreignKeys": {},
40 | "compositePrimaryKeys": {},
41 | "uniqueConstraints": {},
42 | "checkConstraints": {}
43 | }
44 | },
45 | "views": {},
46 | "enums": {},
47 | "_meta": {
48 | "schemas": {},
49 | "tables": {},
50 | "columns": {}
51 | },
52 | "internal": {
53 | "indexes": {}
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/drizzle/meta/_journal.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "7",
3 | "dialect": "sqlite",
4 | "entries": [
5 | {
6 | "idx": 0,
7 | "version": "6",
8 | "when": 1743024718213,
9 | "tag": "0000_lame_kitty_pryde",
10 | "breakpoints": true
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@redwoodjs/starter-drizzle",
3 | "version": "1.0.0",
4 | "description": "A RedwoodSDK starter for projects with a database using Drizzle",
5 | "main": "index.js",
6 | "type": "module",
7 | "keywords": [],
8 | "author": "",
9 | "license": "MIT",
10 | "private": true,
11 | "scripts": {
12 | "build": "vite build",
13 | "dev": "NODE_ENV=${NODE_ENV:-development} vite dev",
14 | "dev:init": "rw-scripts dev-init",
15 | "preview": "bun run build && bun run vite preview",
16 | "worker:run": "rw-scripts worker-run",
17 | "clean": "bun run clean:vite",
18 | "clean:vite": "rm -rf ./node_modules/.vite",
19 | "release": "rw-scripts ensure-deploy-env && bun run clean && bun run migrate:new && RWSDK_DEPLOY=1 bun run build && wrangler deploy",
20 | "format": "prettier --write ./src",
21 | "migrate:new": "drizzle-kit generate",
22 | "migrate:dev": "wrangler d1 migrations apply DB --local",
23 | "seed": "bun run worker:run ./src/db/seed.ts",
24 | "check": "bun run types",
25 | "types": "bun run tsc",
26 | "deploy": "bun run --env-file ../../.env ./alchemy.run.ts",
27 | "destroy": "bun run --env-file ../../.env ./alchemy.run.ts --destroy"
28 | },
29 | "dependencies": {
30 | "alchemy": "workspace:*",
31 | "dotenv": "^16.5.0",
32 | "drizzle-orm": "beta",
33 | "rwsdk": "^0.0.83"
34 | },
35 | "devDependencies": {
36 | "@types/node": "^22.14.1",
37 | "@types/react": "^19.1.2",
38 | "@types/react-dom": "^19.1.2",
39 | "drizzle-kit": "beta",
40 | "tsx": "^4.19.3",
41 | "vite": "^6.3.2",
42 | "wrangler": "^4.12.1"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/public/images/cloudflare-account-id.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-redwood/public/images/cloudflare-account-id.png
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/public/images/cloudflare-copy-token.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-redwood/public/images/cloudflare-copy-token.png
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/public/images/cloudflare-custom-tokens.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-redwood/public/images/cloudflare-custom-tokens.png
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/public/images/cloudflare-d1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-redwood/public/images/cloudflare-d1.png
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/public/images/cloudflare-new-token.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-redwood/public/images/cloudflare-new-token.png
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/public/images/cloudflare-token-summary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-redwood/public/images/cloudflare-token-summary.png
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/public/images/cloudflare-user-api-tokens.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-redwood/public/images/cloudflare-user-api-tokens.png
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/public/images/new-db.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-redwood/public/images/new-db.png
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/src/app/Document.tsx:
--------------------------------------------------------------------------------
1 | export const Document: React.FC<{ children: React.ReactNode }> = ({
2 | children,
3 | }) => (
4 |
5 |
6 |
7 |
8 | @redwoodjs/starter-drizzle
9 |
10 |
11 |
12 | {children}
13 |
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/src/app/headers.ts:
--------------------------------------------------------------------------------
1 | import { IS_DEV } from "rwsdk/constants";
2 | import type { RouteMiddleware } from "rwsdk/router";
3 |
4 | export const setCommonHeaders =
5 | (): RouteMiddleware =>
6 | ({ headers, rw: { nonce } }) => {
7 | if (!IS_DEV) {
8 | // Forces browsers to always use HTTPS for a specified time period (2 years)
9 | headers.set(
10 | "Strict-Transport-Security",
11 | "max-age=63072000; includeSubDomains; preload",
12 | );
13 | }
14 |
15 | // Forces browser to use the declared content-type instead of trying to guess/sniff it
16 | headers.set("X-Content-Type-Options", "nosniff");
17 |
18 | // Stops browsers from sending the referring webpage URL in HTTP headers
19 | headers.set("Referrer-Policy", "no-referrer");
20 |
21 | // Explicitly disables access to specific browser features/APIs
22 | headers.set(
23 | "Permissions-Policy",
24 | "geolocation=(), microphone=(), camera=()",
25 | );
26 |
27 | // Defines trusted sources for content loading and script execution:
28 | headers.set(
29 | "Content-Security-Policy",
30 | `default-src 'self'; script-src 'self' 'nonce-${nonce}' https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; frame-src https://challenges.cloudflare.com; object-src 'none';`,
31 | );
32 | };
33 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/src/app/pages/Home.tsx:
--------------------------------------------------------------------------------
1 | import type { RequestInfo } from "rwsdk/worker";
2 | import { users } from "../../db/schema.js";
3 |
4 | export async function Home({ ctx }: RequestInfo) {
5 | const allUsers = await ctx.db.select().from(users).all();
6 | return (
7 |
8 |
Hello World
9 |
{JSON.stringify(allUsers, null, 2)}
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/src/app/shared/links.ts:
--------------------------------------------------------------------------------
1 | import { defineLinks } from "rwsdk/router";
2 |
3 | export const link = defineLinks(["/"]);
4 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/src/client.tsx:
--------------------------------------------------------------------------------
1 | import { initClient } from "rwsdk/client";
2 |
3 | initClient();
4 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/src/db/schema.ts:
--------------------------------------------------------------------------------
1 | // schema.ts
2 | import { int, sqliteTable, text } from "drizzle-orm/sqlite-core";
3 |
4 | export const users = sqliteTable("users", {
5 | id: int().primaryKey({ autoIncrement: true }),
6 | name: text().notNull(),
7 | email: text().notNull().unique(),
8 | });
9 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/src/db/seed.ts:
--------------------------------------------------------------------------------
1 | import { drizzle } from "drizzle-orm/d1";
2 | import { defineScript } from "rwsdk/worker";
3 | import { users } from "./schema.js";
4 |
5 | export default defineScript(async ({ env }) => {
6 | const db = drizzle(env.DB);
7 |
8 | // Insert a user
9 | await db.insert(users).values({
10 | name: "__change me__",
11 | email: "__change me__",
12 | });
13 |
14 | // Verify the insert by selecting all users
15 | const result = await db.select().from(users).all();
16 |
17 | console.log("🌱 Finished seeding");
18 |
19 | return Response.json(result);
20 | });
21 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/src/worker.tsx:
--------------------------------------------------------------------------------
1 | import { env } from "cloudflare:workers";
2 | import { drizzle } from "drizzle-orm/d1";
3 | import { index, render } from "rwsdk/router";
4 | import { defineApp } from "rwsdk/worker";
5 | import { Document } from "src/Document";
6 | import { setCommonHeaders } from "src/headers";
7 | import { Home } from "src/pages/Home";
8 |
9 | export interface Env {
10 | DB: D1Database;
11 | }
12 |
13 | export type AppContext = {
14 | db: ReturnType;
15 | };
16 |
17 | export default defineApp([
18 | setCommonHeaders(),
19 | ({ ctx }) => {
20 | // setup db in appContext
21 | ctx.db = drizzle(env.DB);
22 | },
23 | render(Document, [index([Home])]),
24 | ]);
25 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/types/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import type { website } from "../alchemy.run.js";
4 |
5 | export type CloudFlareEnv = typeof website.Env;
6 |
7 | declare module "cloudflare:workers" {
8 | namespace Cloudflare {
9 | export interface Env extends CloudFlareEnv {}
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/types/rw.d.ts:
--------------------------------------------------------------------------------
1 | import { AppContext } from "../src/worker.js";
2 |
3 |
4 | declare module "rwsdk/worker" {
5 | export interface DefaultAppContext extends AppContext {}
6 | export interface RequestInfo {
7 | ctx: AppContext
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/types/vite.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*?url" {
2 | const result: string;
3 | export default result;
4 | }
5 |
--------------------------------------------------------------------------------
/examples/cloudflare-redwood/vite.config.mts:
--------------------------------------------------------------------------------
1 | import { redwood } from "rwsdk/vite";
2 | import { defineConfig } from "vite";
3 |
4 | export default defineConfig({
5 | plugins: [redwood()],
6 | });
7 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package-lock.json
3 | yarn.lock
4 |
5 | .DS_Store
6 | .cache
7 | .env
8 | .vercel
9 | .output
10 | .vinxi
11 |
12 | /build/
13 | /api/
14 | /server/build
15 | /public/build
16 | .vinxi
17 | # Sentry Config File
18 | .env.sentry-build-plugin
19 | /test-results/
20 | /playwright-report/
21 | /blob-report/
22 | /playwright/.cache/
23 |
24 | wrangler.jsonc
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/.prettierignore:
--------------------------------------------------------------------------------
1 | **/build
2 | **/public
3 | pnpm-lock.yaml
4 | routeTree.gen.ts
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.watcherExclude": {
3 | "**/routeTree.gen.ts": true
4 | },
5 | "search.exclude": {
6 | "**/routeTree.gen.ts": true
7 | },
8 | "files.readonlyInclude": {
9 | "**/routeTree.gen.ts": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/alchemy.run.ts:
--------------------------------------------------------------------------------
1 | import alchemy from "alchemy";
2 | import { R2RestStateStore, TanStackStart } from "alchemy/cloudflare";
3 |
4 | const BRANCH_PREFIX = process.env.BRANCH_PREFIX ?? "";
5 |
6 | const app = await alchemy("cloudflare-tanstack", {
7 | phase: process.argv.includes("--destroy") ? "destroy" : "up",
8 | stateStore:
9 | process.env.ALCHEMY_STATE_STORE === "cloudflare"
10 | ? (scope) => new R2RestStateStore(scope)
11 | : undefined,
12 | });
13 |
14 | export const website = await TanStackStart(
15 | `cloudflare-tanstack-website${BRANCH_PREFIX}`,
16 | );
17 |
18 | console.log({
19 | url: website.url,
20 | });
21 |
22 | await app.finalize();
23 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/app.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "@tanstack/react-start/config";
2 | import tsConfigPaths from "vite-tsconfig-paths";
3 | import {
4 | cloudflareWorkersDevEnvironmentShim,
5 | external,
6 | } from "../../alchemy/src/cloudflare/index.js";
7 |
8 | export default defineConfig({
9 | tsr: {
10 | appDirectory: "src",
11 | },
12 | server: {
13 | preset: "cloudflare-module",
14 | experimental: {
15 | asyncContext: true,
16 | },
17 | unenv: {
18 | external,
19 | },
20 | },
21 | vite: {
22 | plugins: [
23 | // polyfills import { env } from "cloudflare:workers" during `vite dev` (not deployed to server)
24 | cloudflareWorkersDevEnvironmentShim(),
25 | tsConfigPaths({
26 | projects: ["./tsconfig.json"],
27 | }),
28 | ],
29 | build: {
30 | rollupOptions: {
31 | external,
32 | },
33 | },
34 | },
35 | });
36 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tanstack-start-example-basic",
3 | "private": true,
4 | "sideEffects": false,
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vinxi dev",
8 | "build": "vinxi build",
9 | "start": "vinxi start",
10 | "deploy": "bun run --env-file ../../.env ./alchemy.run.ts",
11 | "destroy": "bun run --env-file ../../.env ./alchemy.run.ts --destroy"
12 | },
13 | "dependencies": {
14 | "@tanstack/react-router": "^1.116.0",
15 | "@tanstack/react-router-devtools": "^1.116.0",
16 | "@tanstack/react-start": "^1.116.1",
17 | "react": "^19.0.0",
18 | "react-dom": "^19.0.0",
19 | "tailwind-merge": "^2.6.0",
20 | "vinxi": "0.5.3"
21 | },
22 | "devDependencies": {
23 | "@types/node": "^22.5.4",
24 | "@types/react-dom": "^19.0.3",
25 | "@types/react": "^19.0.8",
26 | "autoprefixer": "^10.4.20",
27 | "alchemy": "workspace:*",
28 | "cloudflare": "^4.2.0",
29 | "postcss": "^8.5.1",
30 | "tailwindcss": "^3.4.17",
31 | "typescript": "^5.7.2",
32 | "vite-tsconfig-paths": "^5.1.4"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-tanstack-start/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-tanstack-start/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-tanstack-start/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-tanstack-start/public/favicon-16x16.png
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-tanstack-start/public/favicon-32x32.png
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-tanstack-start/public/favicon.ico
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/examples/cloudflare-tanstack-start/public/favicon.png
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/public/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "short_name": "",
4 | "icons": [
5 | {
6 | "src": "/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/api.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createStartAPIHandler,
3 | defaultAPIFileRouteHandler,
4 | } from "@tanstack/react-start/api";
5 |
6 | export default createStartAPIHandler(defaultAPIFileRouteHandler);
7 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/client.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | import { hydrateRoot } from "react-dom/client";
3 | import { StartClient } from "@tanstack/react-start";
4 | import { createRouter } from "./router.js";
5 |
6 | const router = createRouter();
7 |
8 | hydrateRoot(document, );
9 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/components/NotFound.tsx:
--------------------------------------------------------------------------------
1 | import { Link } from "@tanstack/react-router";
2 |
3 | export function NotFound({ children }: { children?: any }) {
4 | return (
5 |
6 |
7 | {children ||
The page you are looking for does not exist.
}
8 |
9 |
10 |
16 |
20 | Start Over
21 |
22 |
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/components/PostError.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | ErrorComponent,
3 | type ErrorComponentProps,
4 | } from "@tanstack/react-router";
5 |
6 | export function PostErrorComponent({ error }: ErrorComponentProps) {
7 | return ;
8 | }
9 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/components/UserError.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | ErrorComponent,
3 | type ErrorComponentProps,
4 | } from "@tanstack/react-router";
5 |
6 | export function UserErrorComponent({ error }: ErrorComponentProps) {
7 | return ;
8 | }
9 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import type { website } from "../alchemy.run.js";
4 |
5 | export type CloudFlareEnv = typeof website.Env;
6 |
7 | declare module "cloudflare:workers" {
8 | namespace Cloudflare {
9 | export interface Env extends CloudFlareEnv {}
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/global-middleware.ts:
--------------------------------------------------------------------------------
1 | import { registerGlobalMiddleware } from "@tanstack/react-start";
2 | import { logMiddleware } from "./utils/loggingMiddleware.js";
3 |
4 | registerGlobalMiddleware({
5 | middleware: [logMiddleware],
6 | });
7 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/router.tsx:
--------------------------------------------------------------------------------
1 | import { createRouter as createTanStackRouter } from "@tanstack/react-router";
2 | import { routeTree } from "./routeTree.gen.js";
3 | import { DefaultCatchBoundary } from "./components/DefaultCatchBoundary.js";
4 | import { NotFound } from "./components/NotFound.js";
5 |
6 | export function createRouter() {
7 | const router = createTanStackRouter({
8 | routeTree,
9 | defaultPreload: "intent",
10 | defaultErrorComponent: DefaultCatchBoundary,
11 | defaultNotFoundComponent: () => ,
12 | scrollRestoration: true,
13 | });
14 |
15 | return router;
16 | }
17 |
18 | declare module "@tanstack/react-router" {
19 | interface Register {
20 | router: ReturnType;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/routes/_pathlessLayout.tsx:
--------------------------------------------------------------------------------
1 | import { Outlet, createFileRoute } from "@tanstack/react-router";
2 |
3 | export const Route = createFileRoute("/_pathlessLayout")({
4 | component: LayoutComponent,
5 | });
6 |
7 | function LayoutComponent() {
8 | return (
9 |
10 |
I'm a layout
11 |
12 |
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/routes/_pathlessLayout/_nested-layout.tsx:
--------------------------------------------------------------------------------
1 | import { Link, Outlet, createFileRoute } from "@tanstack/react-router";
2 |
3 | export const Route = createFileRoute("/_pathlessLayout/_nested-layout")({
4 | component: LayoutComponent,
5 | });
6 |
7 | function LayoutComponent() {
8 | return (
9 |
10 |
I'm a nested layout
11 |
12 |
18 | Go to route A
19 |
20 |
26 | Go to route B
27 |
28 |
29 |
30 |
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/routes/_pathlessLayout/_nested-layout/route-a.tsx:
--------------------------------------------------------------------------------
1 | import { createFileRoute } from "@tanstack/react-router";
2 |
3 | export const Route = createFileRoute("/_pathlessLayout/_nested-layout/route-a")(
4 | {
5 | component: LayoutAComponent,
6 | },
7 | );
8 |
9 | function LayoutAComponent() {
10 | return I'm A!
;
11 | }
12 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/routes/_pathlessLayout/_nested-layout/route-b.tsx:
--------------------------------------------------------------------------------
1 | import { createFileRoute } from "@tanstack/react-router";
2 |
3 | export const Route = createFileRoute("/_pathlessLayout/_nested-layout/route-b")(
4 | {
5 | component: LayoutBComponent,
6 | },
7 | );
8 |
9 | function LayoutBComponent() {
10 | return I'm B!
;
11 | }
12 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/routes/api/users.$id.ts:
--------------------------------------------------------------------------------
1 | import { json } from "@tanstack/react-start";
2 | import { createAPIFileRoute } from "@tanstack/react-start/api";
3 | import type { User } from "../../utils/users.js";
4 |
5 | export const APIRoute = createAPIFileRoute("/api/users/$id")({
6 | GET: async ({ request, params }) => {
7 | console.info(`Fetching users by id=${params.id}... @`, request.url);
8 | try {
9 | const res = await fetch(
10 | `https://jsonplaceholder.typicode.com/users/${params.id}`,
11 | );
12 | if (!res.ok) {
13 | throw new Error("Failed to fetch user");
14 | }
15 |
16 | const user = (await res.json()) as User;
17 |
18 | return json({
19 | id: user.id,
20 | name: user.name,
21 | email: user.email,
22 | });
23 | } catch (e) {
24 | console.error(e);
25 | return json({ error: "User not found" }, { status: 404 });
26 | }
27 | },
28 | });
29 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/routes/api/users.ts:
--------------------------------------------------------------------------------
1 | import { json } from "@tanstack/react-start";
2 | import { createAPIFileRoute } from "@tanstack/react-start/api";
3 | import type { User } from "../../utils/users.js";
4 |
5 | export const APIRoute = createAPIFileRoute("/api/users")({
6 | GET: async ({ request }) => {
7 | console.info("Fetching users... @", request.url);
8 | const res = await fetch("https://jsonplaceholder.typicode.com/users");
9 | if (!res.ok) {
10 | throw new Error("Failed to fetch users");
11 | }
12 |
13 | const data = (await res.json()) as Array;
14 |
15 | const list = data.slice(0, 10);
16 |
17 | return json(list.map((u) => ({ id: u.id, name: u.name, email: u.email })));
18 | },
19 | });
20 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/routes/index.tsx:
--------------------------------------------------------------------------------
1 | import { createFileRoute } from "@tanstack/react-router";
2 |
3 | export const Route = createFileRoute("/")({
4 | component: Home,
5 | });
6 |
7 | function Home() {
8 | return (
9 |
10 |
Welcome Home!!!
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/routes/posts.$postId.tsx:
--------------------------------------------------------------------------------
1 | import { Link, createFileRoute } from "@tanstack/react-router";
2 | import { fetchPost } from "../utils/posts.js";
3 | import { NotFound } from "~/components/NotFound";
4 | import { PostErrorComponent } from "~/components/PostError";
5 |
6 | export const Route = createFileRoute("/posts/$postId")({
7 | loader: ({ params: { postId } }) => fetchPost({ data: postId }),
8 | errorComponent: PostErrorComponent,
9 | component: PostComponent,
10 | notFoundComponent: () => {
11 | return Post not found;
12 | },
13 | });
14 |
15 | function PostComponent() {
16 | const post = Route.useLoaderData();
17 |
18 | return (
19 |
20 |
{post.title}
21 |
{post.body}
22 |
30 | Deep View
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/routes/posts.index.tsx:
--------------------------------------------------------------------------------
1 | import { createFileRoute } from "@tanstack/react-router";
2 |
3 | export const Route = createFileRoute("/posts/")({
4 | component: PostsIndexComponent,
5 | });
6 |
7 | function PostsIndexComponent() {
8 | return Select a post.
;
9 | }
10 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/routes/posts.route.tsx:
--------------------------------------------------------------------------------
1 | import { Link, Outlet, createFileRoute } from "@tanstack/react-router";
2 | import { fetchPosts } from "../utils/posts.js";
3 |
4 | export const Route = createFileRoute("/posts")({
5 | loader: async () => fetchPosts(),
6 | component: PostsLayoutComponent,
7 | });
8 |
9 | function PostsLayoutComponent() {
10 | const posts = Route.useLoaderData();
11 |
12 | return (
13 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/routes/posts_.$postId.deep.tsx:
--------------------------------------------------------------------------------
1 | import { Link, createFileRoute } from "@tanstack/react-router";
2 | import { fetchPost } from "../utils/posts.js";
3 | import { PostErrorComponent } from "~/components/PostError";
4 |
5 | export const Route = createFileRoute("/posts_/$postId/deep")({
6 | loader: async ({ params: { postId } }) =>
7 | fetchPost({
8 | data: postId,
9 | }),
10 | errorComponent: PostErrorComponent,
11 | component: PostDeepComponent,
12 | });
13 |
14 | function PostDeepComponent() {
15 | const post = Route.useLoaderData();
16 |
17 | return (
18 |
19 |
23 | ← All Posts
24 |
25 |
{post.title}
26 |
{post.body}
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/routes/redirect.tsx:
--------------------------------------------------------------------------------
1 | import { createFileRoute, redirect } from "@tanstack/react-router";
2 |
3 | export const Route = createFileRoute("/redirect")({
4 | beforeLoad: async () => {
5 | throw redirect({
6 | to: "/posts",
7 | });
8 | },
9 | });
10 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/routes/users.$userId.tsx:
--------------------------------------------------------------------------------
1 | import { createFileRoute } from "@tanstack/react-router";
2 | import type { User } from "~/utils/users";
3 | import { DEPLOY_URL } from "~/utils/users";
4 | import { NotFound } from "~/components/NotFound";
5 | import { UserErrorComponent } from "~/components/UserError";
6 |
7 | export const Route = createFileRoute("/users/$userId")({
8 | loader: async ({ params: { userId } }) => {
9 | try {
10 | const res = await fetch(`${DEPLOY_URL}/api/users/${userId}`);
11 | if (!res.ok) {
12 | throw new Error("Unexpected status code");
13 | }
14 |
15 | const data = (await res.json()) as User;
16 |
17 | return data;
18 | } catch {
19 | throw new Error("Failed to fetch user");
20 | }
21 | },
22 | errorComponent: UserErrorComponent,
23 | component: UserComponent,
24 | notFoundComponent: () => {
25 | return User not found;
26 | },
27 | });
28 |
29 | function UserComponent() {
30 | const user = Route.useLoaderData();
31 |
32 | return (
33 |
34 |
{user.name}
35 |
{user.email}
36 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/routes/users.index.tsx:
--------------------------------------------------------------------------------
1 | import { createFileRoute } from "@tanstack/react-router";
2 |
3 | export const Route = createFileRoute("/users/")({
4 | component: UsersIndexComponent,
5 | });
6 |
7 | function UsersIndexComponent() {
8 | return Select a user.
;
9 | }
10 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/routes/users.route.tsx:
--------------------------------------------------------------------------------
1 | import { Link, Outlet, createFileRoute } from "@tanstack/react-router";
2 | import { DEPLOY_URL } from "../utils/users.js";
3 | import type { User } from "../utils/users.js";
4 |
5 | export const Route = createFileRoute("/users")({
6 | loader: async () => {
7 | try {
8 | const res = await fetch(`${DEPLOY_URL}/api/users`);
9 | if (!res.ok) {
10 | throw new Error("Unexpected status code");
11 | }
12 |
13 | const data = (await res.json()) as Array;
14 |
15 | return data;
16 | } catch {
17 | throw new Error("Failed to fetch users");
18 | }
19 | },
20 | component: UsersLayoutComponent,
21 | });
22 |
23 | function UsersLayoutComponent() {
24 | const users = Route.useLoaderData();
25 |
26 | return (
27 |
28 |
29 | {[
30 | ...users,
31 | { id: "i-do-not-exist", name: "Non-existent User", email: "" },
32 | ].map((user) => {
33 | return (
34 | -
35 |
43 |
{user.name}
44 |
45 |
46 | );
47 | })}
48 |
49 |
50 |
51 |
52 | );
53 | }
54 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/ssr.tsx:
--------------------------------------------------------------------------------
1 | ///
2 | import {
3 | createStartHandler,
4 | defaultStreamHandler,
5 | } from "@tanstack/react-start/server";
6 | import { getRouterManifest } from "@tanstack/react-start/router-manifest";
7 |
8 | import { createRouter } from "./router.js";
9 |
10 | export default createStartHandler({
11 | createRouter,
12 | getRouterManifest,
13 | })(defaultStreamHandler);
14 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/styles/app.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | html {
7 | color-scheme: light dark;
8 | }
9 |
10 | * {
11 | @apply border-gray-200 dark:border-gray-800;
12 | }
13 |
14 | html,
15 | body {
16 | @apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200;
17 | }
18 |
19 | .using-mouse * {
20 | outline: none !important;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/utils/loggingMiddleware.tsx:
--------------------------------------------------------------------------------
1 | import { createMiddleware } from "@tanstack/react-start";
2 |
3 | const preLogMiddleware = createMiddleware()
4 | .client(async (ctx) => {
5 | const clientTime = new Date();
6 |
7 | return ctx.next({
8 | context: {
9 | clientTime,
10 | },
11 | sendContext: {
12 | clientTime,
13 | },
14 | });
15 | })
16 | .server(async (ctx) => {
17 | const serverTime = new Date();
18 |
19 | return ctx.next({
20 | sendContext: {
21 | serverTime,
22 | durationToServer:
23 | serverTime.getTime() - ctx.context.clientTime.getTime(),
24 | },
25 | });
26 | });
27 |
28 | export const logMiddleware = createMiddleware()
29 | .middleware([preLogMiddleware])
30 | .client(async (ctx) => {
31 | const res = await ctx.next();
32 |
33 | const now = new Date();
34 | console.log("Client Req/Res:", {
35 | duration: res.context.clientTime.getTime() - now.getTime(),
36 | durationToServer: res.context.durationToServer,
37 | durationFromServer: now.getTime() - res.context.serverTime.getTime(),
38 | });
39 |
40 | return res;
41 | });
42 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/utils/posts.tsx:
--------------------------------------------------------------------------------
1 | import { notFound } from "@tanstack/react-router";
2 | import { createServerFn } from "@tanstack/react-start";
3 |
4 | export type PostType = {
5 | id: string;
6 | title: string;
7 | body: string;
8 | };
9 |
10 | export const fetchPost = createServerFn({ method: "GET" })
11 | .validator((d: string) => d)
12 | .handler(async ({ data }) => {
13 | console.info(`Fetching post with id ${data}...`);
14 | const res = await fetch(
15 | `https://jsonplaceholder.typicode.com/posts/${data}`,
16 | );
17 | if (!res.ok) {
18 | if (res.status === 404) {
19 | throw notFound();
20 | }
21 |
22 | throw new Error("Failed to fetch post");
23 | }
24 |
25 | const post = (await res.json()) as PostType;
26 |
27 | return post;
28 | });
29 |
30 | export const fetchPosts = createServerFn({ method: "GET" }).handler(
31 | async () => {
32 | console.info("Fetching posts...");
33 | const res = await fetch("https://jsonplaceholder.typicode.com/posts");
34 | if (!res.ok) {
35 | throw new Error("Failed to fetch posts");
36 | }
37 |
38 | const posts = (await res.json()) as Array;
39 |
40 | return posts;
41 | },
42 | );
43 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/utils/seo.ts:
--------------------------------------------------------------------------------
1 | export const seo = ({
2 | title,
3 | description,
4 | keywords,
5 | image,
6 | }: {
7 | title: string;
8 | description?: string;
9 | image?: string;
10 | keywords?: string;
11 | }) => {
12 | const tags = [
13 | { title },
14 | { name: "description", content: description },
15 | { name: "keywords", content: keywords },
16 | { name: "twitter:title", content: title },
17 | { name: "twitter:description", content: description },
18 | { name: "twitter:creator", content: "@tannerlinsley" },
19 | { name: "twitter:site", content: "@tannerlinsley" },
20 | { name: "og:type", content: "website" },
21 | { name: "og:title", content: title },
22 | { name: "og:description", content: description },
23 | ...(image
24 | ? [
25 | { name: "twitter:image", content: image },
26 | { name: "twitter:card", content: "summary_large_image" },
27 | { name: "og:image", content: image },
28 | ]
29 | : []),
30 | ];
31 |
32 | return tags;
33 | };
34 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/src/utils/users.tsx:
--------------------------------------------------------------------------------
1 | export type User = {
2 | id: number;
3 | name: string;
4 | email: string;
5 | };
6 |
7 | // must check if window is not undefined since the bundler also places this code server-side
8 | export const DEPLOY_URL =
9 | typeof window !== "undefined"
10 | ? window.location.origin
11 | : "http://localhost:3000";
12 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/tailwind.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | content: ['./src/**/*.{js,jsx,ts,tsx}'],
4 | }
5 |
--------------------------------------------------------------------------------
/examples/cloudflare-tanstack-start/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["**/*.ts", "**/*.tsx"],
3 | "references": [{ "path": "../../alchemy/tsconfig.json" }],
4 | "compilerOptions": {
5 | "strict": true,
6 | "esModuleInterop": true,
7 | "jsx": "react-jsx",
8 | "module": "ESNext",
9 | "moduleResolution": "Bundler",
10 | "lib": ["DOM", "DOM.Iterable", "ES2022"],
11 | "isolatedModules": true,
12 | "resolveJsonModule": true,
13 | "skipLibCheck": true,
14 | "target": "ES2022",
15 | "allowJs": true,
16 | "forceConsistentCasingInFileNames": true,
17 | "baseUrl": ".",
18 | "paths": {
19 | "~/*": ["./src/*"]
20 | },
21 | "noImplicitAny": false,
22 | "types": ["@cloudflare/workers-types"],
23 | "noEmit": true
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/examples/cloudflare-vite/.gitignore:
--------------------------------------------------------------------------------
1 | /wrangler.*
2 | /.wrangler
--------------------------------------------------------------------------------
/examples/cloudflare-vite/alchemy.run.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import alchemy from "alchemy";
4 | import {
5 | KVNamespace,
6 | R2Bucket,
7 | R2RestStateStore,
8 | Vite,
9 | } from "alchemy/cloudflare";
10 |
11 | const BRANCH_PREFIX = process.env.BRANCH_PREFIX ?? "";
12 | const app = await alchemy("cloudflare-vite", {
13 | stage: process.env.USER ?? "dev",
14 | phase: process.argv.includes("--destroy") ? "destroy" : "up",
15 | quiet: !process.argv.includes("--verbose"),
16 | password: process.env.ALCHEMY_PASSWORD,
17 | stateStore:
18 | process.env.ALCHEMY_STATE_STORE === "cloudflare"
19 | ? (scope) => new R2RestStateStore(scope)
20 | : undefined,
21 | });
22 |
23 | export const [authStore, storage] = await Promise.all([
24 | KVNamespace("AUTH_STORE", {
25 | title: `cloudflare-vite-auth-store${BRANCH_PREFIX}`,
26 | adopt: true,
27 | }),
28 | R2Bucket(`cloudflare-vite-storage${BRANCH_PREFIX}`, {
29 | allowPublicAccess: false,
30 | // so that CI is idempotent
31 | adopt: true,
32 | }),
33 | ]);
34 |
35 | export const website = await Vite(`cloudflare-vite-website${BRANCH_PREFIX}`, {
36 | main: "./src/index.ts",
37 | command: "bun run build",
38 | bindings: {
39 | STORAGE: storage,
40 | AUTH_STORE: authStore,
41 | },
42 | });
43 |
44 | console.log({
45 | url: website.url,
46 | });
47 |
48 | await app.finalize();
49 |
--------------------------------------------------------------------------------
/examples/cloudflare-vite/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/cloudflare-vite/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-project",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "deploy": "bun run --env-file ../../.env ./alchemy.run.ts",
10 | "destroy": "bun run --env-file ../../.env ./alchemy.run.ts --destroy",
11 | "lint": "eslint .",
12 | "preview": "vite preview"
13 | },
14 | "dependencies": {
15 | "@openauthjs/openauth": "^0.4.3",
16 | "react": "^19.0.0",
17 | "react-dom": "^19.0.0",
18 | "valibot": "^1.0.0"
19 | },
20 | "devDependencies": {
21 | "@cloudflare/vite-plugin": "^1.0.4",
22 | "@eslint/js": "^9.21.0",
23 | "@types/node": "^22.13.10",
24 | "@types/react": "^19.0.10",
25 | "@types/react-dom": "^19.0.4",
26 | "@vitejs/plugin-react": "^4.3.4",
27 | "alchemy": "workspace:*",
28 | "cloudflare": "^4.2.0",
29 | "dotenv": "^16.4.7",
30 | "eslint": "^9.21.0",
31 | "eslint-plugin-react-hooks": "^5.1.0",
32 | "eslint-plugin-react-refresh": "^0.4.19",
33 | "globals": "^15.15.0",
34 | "tsx": "^4.19.3",
35 | "typescript": "~5.7.2",
36 | "typescript-eslint": "^8.24.1",
37 | "vite": "^6.2.0"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/examples/cloudflare-vite/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/cloudflare-vite/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
8 | .logo {
9 | height: 6em;
10 | padding: 1.5em;
11 | will-change: filter;
12 | transition: filter 300ms;
13 | }
14 | .logo:hover {
15 | filter: drop-shadow(0 0 2em #646cffaa);
16 | }
17 | .logo.react:hover {
18 | filter: drop-shadow(0 0 2em #61dafbaa);
19 | }
20 |
21 | @keyframes logo-spin {
22 | from {
23 | transform: rotate(0deg);
24 | }
25 | to {
26 | transform: rotate(360deg);
27 | }
28 | }
29 |
30 | @media (prefers-reduced-motion: no-preference) {
31 | a:nth-of-type(2) .logo {
32 | animation: logo-spin infinite 20s linear;
33 | }
34 | }
35 |
36 | .card {
37 | padding: 2em;
38 | }
39 |
40 | .read-the-docs {
41 | color: #888;
42 | }
43 |
--------------------------------------------------------------------------------
/examples/cloudflare-vite/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import reactLogo from "./assets/react.svg";
3 | import viteLogo from "/vite.svg";
4 | import "./App.css";
5 |
6 | function App() {
7 | const [count, setCount] = useState(0);
8 |
9 | return (
10 | <>
11 |
19 | Vite + React
20 |
21 |
24 |
25 | Edit src/App.tsx
and save to test HMR
26 |
27 |
28 |
29 | Click on the Vite and React logos to learn more
30 |
31 | >
32 | );
33 | }
34 |
35 | export default App;
36 |
--------------------------------------------------------------------------------
/examples/cloudflare-vite/src/auth/subjects.ts:
--------------------------------------------------------------------------------
1 | import { createSubjects } from "@openauthjs/openauth/subject";
2 | import type { InferOutput } from "valibot";
3 | import { object, string } from "valibot";
4 |
5 | export type User = InferOutput;
6 |
7 | export const User = object({
8 | id: string(),
9 | name: string(),
10 | email: string(),
11 | avatar: string(),
12 | });
13 |
14 | export interface Subjects {
15 | user: InferOutput;
16 | }
17 |
18 | // Define our subject structure
19 | export const Subjects = createSubjects({
20 | user: User,
21 | });
22 |
--------------------------------------------------------------------------------
/examples/cloudflare-vite/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import type { website } from "../alchemy.run.js";
4 |
5 | export type CloudFlareEnv = typeof website.Env;
6 |
7 | declare module "cloudflare:workers" {
8 | namespace Cloudflare {
9 | export interface Env extends CloudFlareEnv {}
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/examples/cloudflare-vite/src/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
3 | line-height: 1.5;
4 | font-weight: 400;
5 |
6 | color-scheme: light dark;
7 | color: rgba(255, 255, 255, 0.87);
8 | background-color: #242424;
9 |
10 | font-synthesis: none;
11 | text-rendering: optimizeLegibility;
12 | -webkit-font-smoothing: antialiased;
13 | -moz-osx-font-smoothing: grayscale;
14 | }
15 |
16 | a {
17 | font-weight: 500;
18 | color: #646cff;
19 | text-decoration: inherit;
20 | }
21 | a:hover {
22 | color: #535bf2;
23 | }
24 |
25 | body {
26 | margin: 0;
27 | display: flex;
28 | place-items: center;
29 | min-width: 320px;
30 | min-height: 100vh;
31 | }
32 |
33 | h1 {
34 | font-size: 3.2em;
35 | line-height: 1.1;
36 | }
37 |
38 | button {
39 | border-radius: 8px;
40 | border: 1px solid transparent;
41 | padding: 0.6em 1.2em;
42 | font-size: 1em;
43 | font-weight: 500;
44 | font-family: inherit;
45 | background-color: #1a1a1a;
46 | cursor: pointer;
47 | transition: border-color 0.25s;
48 | }
49 | button:hover {
50 | border-color: #646cff;
51 | }
52 | button:focus,
53 | button:focus-visible {
54 | outline: 4px auto -webkit-focus-ring-color;
55 | }
56 |
57 | @media (prefers-color-scheme: light) {
58 | :root {
59 | color: #213547;
60 | background-color: #ffffff;
61 | }
62 | a:hover {
63 | color: #747bff;
64 | }
65 | button {
66 | background-color: #f9f9f9;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/examples/cloudflare-vite/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Hono } from "hono";
2 |
3 | // TODO: looks like openauth imports node:fs ...
4 | // import { issuer } from "./auth/issuer";
5 |
6 | export const api = new Hono();
7 |
8 | api.get("/hello", (c) => c.text("Hello World"));
9 |
10 | export default {
11 | async fetch(request: Request): Promise {
12 | return api.fetch(request);
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/examples/cloudflare-vite/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from "react";
2 | import { createRoot } from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App.tsx";
5 |
6 | createRoot(document.getElementById("root")!).render(
7 |
8 |
9 | ,
10 | );
11 |
--------------------------------------------------------------------------------
/examples/cloudflare-vite/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/examples/cloudflare-vite/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "types": ["@cloudflare/workers-types"],
5 | "allowImportingTsExtensions": true,
6 | "jsx": "react-jsx"
7 | },
8 | "include": ["vite/*.ts", "src/**/*.ts", "src/**/*.tsx", "src/env.d.ts"],
9 | "references": [{ "path": "../../alchemy/tsconfig.json" }]
10 | }
11 |
--------------------------------------------------------------------------------
/examples/cloudflare-vite/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { cloudflare } from "@cloudflare/vite-plugin";
2 | import react from "@vitejs/plugin-react";
3 | import { defineConfig } from "vite";
4 |
5 | // https://vite.dev/config/
6 | export default defineConfig({
7 | plugins: [react(), cloudflare()],
8 | });
9 |
--------------------------------------------------------------------------------
/examples/cloudflare-worker-bootstrap/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cloudflare-worker-bootstrap",
3 | "private": true,
4 | "type": "module",
5 | "scripts": {
6 | "deploy": "bun tsx --env-file ../../.env ./index.ts",
7 | "destroy": "bun tsx --env-file ../../.env ./index.ts --destroy"
8 | },
9 | "dependencies": {
10 | "alchemy": "workspace:*"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/examples/cloudflare-worker-bootstrap/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["index.ts"],
3 | "extends": "../../tsconfig.base.json",
4 | "compilerOptions": {
5 | "types": ["@cloudflare/workers-types", "@types/node"]
6 | },
7 | "references": [
8 | {
9 | "path": "../../alchemy/tsconfig.json"
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/examples/cloudflare-worker/.gitignore:
--------------------------------------------------------------------------------
1 | wrangler.jsonc
--------------------------------------------------------------------------------
/examples/cloudflare-worker/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cloudflare-worker",
3 | "private": true,
4 | "type": "module",
5 | "scripts": {
6 | "deploy": "bun run --env-file ../../.env ./alchemy.run.ts",
7 | "destroy": "bun run --env-file ../../.env ./alchemy.run.ts --destroy"
8 | },
9 | "dependencies": {
10 | "alchemy": "workspace:*",
11 | "braintrust": "^0.0.201",
12 | "chalk": "^5.4.1",
13 | "pluralize": "^8.0.0",
14 | "cli-progress": "^3.12.0"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/cloudflare-worker/src/do.ts:
--------------------------------------------------------------------------------
1 | import { DurableObject } from "cloudflare:workers";
2 |
3 | /**
4 | * A simple Hello World Durable Object
5 | */
6 | export class HelloWorldDO extends DurableObject {
7 | async increment() {
8 | // Get the current count from storage or initialize to 0
9 | let count = (await this.ctx.storage.get("count")) || 0;
10 |
11 | // Store the updated count
12 | await this.ctx.storage.put("count", count);
13 |
14 | return count;
15 | }
16 |
17 | /**
18 | * Handle HTTP requests to the Durable Object
19 | */
20 | async fetch(_request: Request): Promise {
21 | const count = await this.increment();
22 |
23 | // Return a response with the count
24 | return new Response(
25 | JSON.stringify({
26 | message: "Hello World from Durable Object!",
27 | count: count,
28 | timestamp: new Date().toISOString(),
29 | }),
30 | {
31 | headers: {
32 | "Content-Type": "application/json",
33 | },
34 | },
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/examples/cloudflare-worker/src/env.ts:
--------------------------------------------------------------------------------
1 | import type { worker } from "../alchemy.run.js";
2 |
3 | declare global {
4 | export type CloudflareEnv = typeof worker.Env;
5 | }
6 |
7 | declare module "cloudflare:workers" {
8 | namespace Cloudflare {
9 | export interface Env extends CloudflareEnv {}
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/examples/cloudflare-worker/src/rpc.ts:
--------------------------------------------------------------------------------
1 | import { WorkerEntrypoint } from "cloudflare:workers";
2 |
3 | export default class MyRPC extends WorkerEntrypoint {
4 | /**
5 | * Hello world
6 | */
7 | async hello(name: string) {
8 | return `Hello, ${name}!`;
9 | }
10 | async fetch() {
11 | return new Response("Hello from Worker B");
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/cloudflare-worker/src/worker.ts:
--------------------------------------------------------------------------------
1 | import { env } from "cloudflare:workers";
2 | import type { queue, worker } from "../alchemy.run.js";
3 | export * from "./do.js";
4 | export * from "./workflow.js";
5 |
6 | export default {
7 | async fetch(_request: Request) {
8 | await env.QUEUE.send({
9 | name: "John Doe",
10 | email: "john.doe@example.com",
11 | });
12 |
13 | const obj = env.DO.get(env.DO.idFromName("foo"));
14 | await obj.increment();
15 | async function _foo() {
16 | // @ts-expect-error - foo doesn't exist on the HelloWorldDO class
17 | await obj.foo();
18 | }
19 | await obj.fetch(new Request("https://example.com"));
20 |
21 | await env.RPC.hello("John Doe");
22 |
23 | return new Response("Ok");
24 | },
25 | async queue(batch: typeof queue.Batch, _env: typeof worker.Env) {
26 | for (const message of batch.messages) {
27 | console.log(message);
28 | message.ack();
29 | }
30 | batch.ackAll();
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/examples/cloudflare-worker/src/workflow.ts:
--------------------------------------------------------------------------------
1 | // Import the Workflow definition
2 | import {
3 | WorkflowEntrypoint,
4 | type WorkflowEvent,
5 | type WorkflowStep,
6 | } from "cloudflare:workers";
7 | // just to test bundling
8 | import { NonRetryableError } from "cloudflare:workflows";
9 |
10 | // Create your own class that implements a Workflow
11 | export class OFACWorkflow extends WorkflowEntrypoint {
12 | // Define a run() method
13 | async run(_event: WorkflowEvent, step: WorkflowStep) {
14 | // Define one or more steps that optionally return state.
15 | await step.do("my first step", async () => {
16 | console.log("OFAC WORKFLOW STEP 1");
17 | });
18 | await step.do("my second step", async () => {
19 | console.log("OFAC WORKFLOW STEP 2");
20 | });
21 |
22 | throw new NonRetryableError("test");
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/cloudflare-worker/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["src/*.ts"],
3 | "extends": "../../tsconfig.base.json",
4 | "compilerOptions": {
5 | "types": ["@cloudflare/workers-types", "@types/node"]
6 | },
7 | "references": [
8 | {
9 | "path": "../../alchemy/tsconfig.json"
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "alchemy-mono",
3 | "version": "0.0.0",
4 | "private": true,
5 | "type": "module",
6 | "module": "./lib/index.js",
7 | "license": "Apache-2.0",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/sam-goodwin/alchemy"
11 | },
12 | "scripts": {
13 | "build": "tsc -b",
14 | "check": "bun run --filter cloudflare-react-router types && tsc -b ./tsconfig.json && biome check",
15 | "fix": "biome check --write",
16 | "deploy:repo": "bun ./stacks/repo.run.ts",
17 | "deploy:website": "bun ./stacks/website.run.ts",
18 | "generate:docs": "bun ./stacks/docs.run.ts",
19 | "generate:aws-control": "bun ./scripts/generate-aws-control.ts",
20 | "publish:npm": "bun run --filter alchemy publish:npm",
21 | "test": "bun ./alchemy/test/run.ts",
22 | "test:force": "vitest run",
23 | "bump": "bun ./scripts/bump.ts"
24 | },
25 | "workspaces": [
26 | "alchemy",
27 | "alchemy-web",
28 | "examples/*"
29 | ],
30 | "devDependencies": {
31 | "@biomejs/biome": "beta",
32 | "@redwoodjs/sdk": "^0.0.80",
33 | "@types/bun": "latest",
34 | "@types/node": "latest",
35 | "@vitest/ui": "^3.1.4",
36 | "aws4fetch": "^1.0.20",
37 | "braintrust": "*",
38 | "changelogithub": "^13.15.0",
39 | "openai": "^4.103.0",
40 | "typescript": "latest",
41 | "vitest": "^3.1.4",
42 | "yaml": "^2.7.1"
43 | },
44 | "dependencies": {
45 | "alchemy-mono": "."
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/public/alchemist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/public/alchemist.png
--------------------------------------------------------------------------------
/public/alchemist.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sam-goodwin/alchemy/5ad508201ef5217e8a5ef47a9efc21e2c29b8031/public/alchemist.webp
--------------------------------------------------------------------------------
/scripts/shell.ts:
--------------------------------------------------------------------------------
1 | import { exec } from "../alchemy/src/os/exec.js";
2 | import { website } from "../stacks/website.run.js";
3 |
4 | const command = process.argv.slice(2).join(" ");
5 |
6 | if (!command) {
7 | console.error("No command provided");
8 | process.exit(1);
9 | }
10 |
11 | await exec(command, {
12 | env: {
13 | WEBSITE_URL: website.url,
14 | },
15 | });
16 |
--------------------------------------------------------------------------------
/stacks/env.ts:
--------------------------------------------------------------------------------
1 | import type { AlchemyOptions, Phase } from "../alchemy/src/alchemy.js";
2 | import { R2RestStateStore } from "../alchemy/src/cloudflare/index.js";
3 | import alchemy from "../alchemy/src/index.js";
4 |
5 | export const CLOUDFLARE_EMAIL = await alchemy.env.CLOUDFLARE_EMAIL;
6 |
7 | export const CLOUDFLARE_ACCOUNT_ID = await alchemy.env.CLOUDFLARE_ACCOUNT_ID;
8 |
9 | export const CLOUDFLARE_API_KEY = await alchemy.secret.env.CLOUDFLARE_API_KEY;
10 |
11 | export const STRIPE_API_KEY = await alchemy.secret.env.STRIPE_API_KEY;
12 |
13 | export const OPENAI_API_KEY = await alchemy.secret.env.OPENAI_API_KEY;
14 |
15 | export const NEON_API_KEY = await alchemy.secret.env.NEON_API_KEY;
16 |
17 | export const UPSTASH_API_KEY = await alchemy.secret.env.UPSTASH_API_KEY;
18 |
19 | export default {
20 | stage: "prod",
21 | phase:
22 | (process.env.ALCHEMY_PHASE as Phase) ??
23 | (process.argv.includes("--destroy")
24 | ? "destroy"
25 | : process.argv.includes("--read")
26 | ? "read"
27 | : "up"),
28 | // pass the password in (you can get it from anywhere, e.g. stdin)
29 | password: process.env.SECRET_PASSPHRASE,
30 | quiet: process.argv.includes("--quiet"),
31 | stateStore:
32 | process.env.ALCHEMY_STATE_STORE === "cloudflare"
33 | ? (scope) => new R2RestStateStore(scope)
34 | : undefined,
35 | } satisfies AlchemyOptions;
36 |
--------------------------------------------------------------------------------
/stacks/website.run.ts:
--------------------------------------------------------------------------------
1 | // ensure providers are registered (for deletion purposes)
2 |
3 | import "../alchemy/src/cloudflare/index.js";
4 | import "../alchemy/src/dns/index.js";
5 | import "../alchemy/src/os/index.js";
6 |
7 | import path from "node:path";
8 | import {
9 | Assets,
10 | CustomDomain,
11 | Worker,
12 | Zone,
13 | } from "../alchemy/src/cloudflare/index.js";
14 | import alchemy from "../alchemy/src/index.js";
15 | import { Exec } from "../alchemy/src/os/index.js";
16 | import options from "./env.js";
17 |
18 | const app = await alchemy("alchemy:website", options);
19 |
20 | const zone = await Zone("alchemy.run", {
21 | name: "alchemy.run",
22 | type: "full",
23 | });
24 |
25 | await Exec("build-site", {
26 | command: "bun run --filter alchemy-web docs:build",
27 | });
28 |
29 | const staticAssets = await Assets("static-assets", {
30 | path: path.join("alchemy-web", ".vitepress", "dist"),
31 | });
32 |
33 | export const website = await Worker("website", {
34 | name: "alchemy-website",
35 | url: true,
36 | bindings: {
37 | ASSETS: staticAssets,
38 | },
39 | assets: {
40 | html_handling: "auto-trailing-slash",
41 | // not_found_handling: "single-page-application",
42 | run_worker_first: false,
43 | },
44 | script: `
45 | export default {
46 | async fetch(request, env) {
47 | // return env.ASSETS.fetch(request);
48 | return new Response("Not Found", { status: 404 });
49 | },
50 | };
51 | `,
52 | });
53 |
54 | await CustomDomain("alchemy-web-domain", {
55 | name: "alchemy.run",
56 | zoneId: zone.id,
57 | workerName: website.name,
58 | });
59 |
60 | await app.finalize();
61 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "exclude": ["node_modules", "dist"],
3 | "compilerOptions": {
4 | "types": ["@cloudflare/workers-types", "@types/node"],
5 | // Enable latest features
6 | "lib": ["ESNext", "DOM"],
7 | "target": "ESNext",
8 | "moduleDetection": "force",
9 | "jsx": "react-jsx",
10 | "allowJs": true,
11 | "esModuleInterop": true,
12 | "noEmit": true,
13 |
14 | // Bundler mode
15 | "module": "Preserve",
16 | "moduleResolution": "Bundler",
17 | // "allowImportingTsExtensions": true,
18 | "verbatimModuleSyntax": true,
19 |
20 | // Best practices
21 | "strict": true,
22 | "skipLibCheck": true,
23 | "noFallthroughCasesInSwitch": true,
24 |
25 | // Some stricter flags (disabled by default)
26 | "noUnusedLocals": false,
27 | "noUnusedParameters": false,
28 | "noPropertyAccessFromIndexSignature": false,
29 |
30 | "noImplicitThis": true
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "exclude": ["node_modules", "dist"],
4 | "references": [
5 | { "path": "./tsconfig.stacks.json" },
6 | { "path": "./alchemy/tsconfig.json" },
7 | { "path": "./alchemy/tsconfig.test.json" },
8 | { "path": "./examples/aws-app/tsconfig.json" },
9 | { "path": "./examples/cloudflare-nuxt-pipeline/tsconfig.json" },
10 | { "path": "./examples/cloudflare-react-router/tsconfig.json" },
11 | { "path": "./examples/cloudflare-redwood/tsconfig.json" },
12 | { "path": "./examples/cloudflare-tanstack-start/tsconfig.json" },
13 | { "path": "./examples/cloudflare-vite/tsconfig.json" },
14 | { "path": "./examples/cloudflare-worker-bootstrap/tsconfig.json" },
15 | { "path": "./examples/cloudflare-worker/tsconfig.json" },
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/tsconfig.stacks.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base.json",
3 | "include": ["./stacks/**/*.ts", "scripts/shell.ts"],
4 | "compilerOptions": {
5 | "noEmit": true
6 | },
7 | "references": [{ "path": "./alchemy/tsconfig.json" }]
8 | }
9 |
--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vitest/config";
2 |
3 |
4 | export default defineConfig({
5 | test: {
6 | pool: "threads",
7 | poolOptions: {
8 | threads: {
9 | singleThread: false,
10 | maxThreads: 32,
11 | },
12 | },
13 | minWorkers: 16,
14 | testTimeout: 120000,
15 | hookTimeout: 120000,
16 | passWithNoTests: true,
17 | sequence: {
18 | concurrent: true,
19 | },
20 | include: ["alchemy/test/**/*.test.ts"],
21 | exclude: [
22 | "**/node_modules/**",
23 | "**/dist/**",
24 | "**/lib/**",
25 | "**/.{idea,git,cache,output,temp}/**",
26 | ],
27 | env: {
28 | NODE_ENV: "test",
29 | },
30 | globals: true,
31 | // reporter: ['verbose'],
32 | coverage: {
33 | provider: "v8",
34 | reporter: ["text", "json", "html"],
35 | exclude: [
36 | "coverage/**",
37 | "dist/**",
38 | "lib/**",
39 | "**/node_modules/**",
40 | "**/*.test.ts",
41 | "**/*.config.*",
42 | ],
43 | },
44 | setupFiles: ["./vitest.setup.ts"],
45 | },
46 | });
47 |
--------------------------------------------------------------------------------
/vitest.setup.ts:
--------------------------------------------------------------------------------
1 | import { config } from "dotenv";
2 | config({ path: ".env" });
3 |
--------------------------------------------------------------------------------