# Rank Examples Bundle > Curated runnable Rank examples for common tasks such as configuration generation, external inputs, providers, HTTP serving, and S3-backed log inspection. Task-oriented Rank examples bundle. Use this file when you want runnable reference material for common workflows instead of the full documentation corpus. ## Examples Runnable Rank programs organized by theme. Each example lives in the [`examples/`](https://github.com/rank-lang/rank/tree/main/examples) directory of the repository. Use this page when you want runnable reference programs for typed configuration, provider-backed reads and writes, external inputs, manifests, and HTTP workflows. If you want the fastest language tour, start with [Hello Config](./hello-config.mdx), then [Functions and Lifting](./functions-and-lifting.mdx), then [Pipes and Collections](./pipes-and-collections.mdx), and then [Env Inputs](./env-inputs.mdx). ## Language features | Example | What it shows | |---------|--------------| | [Hello Config](./hello-config) | Type aliases, literal unions, `@constraint`, `with`, `pub main` | | [Types and Generics](./types-and-generics) | Type aliases, constrained/defaulted generics, utility types, field annotations, `match` exhaustiveness | | [Functions and Lifting](./functions-and-lifting) | Named functions, object lifting, zipped list lifting, `with` patch | | [Pipes and Collections](./pipes-and-collections) | `map`, `filter`, `reduce`, `groupBy`, `sortBy`, pipe operator `|>` | ## Platform manifests and CI/CD | Example | What it shows | |---------|--------------| | [Kubernetes App](./kubernetes-app) | One typed app definition emitting Deployment, Service, ConfigMap, Ingress, and HPA YAML | | [Service Catalog](./service-catalog) | One typed service list emitting service summary, CI matrix, public endpoints, Compose, and deployment metadata | | [Static Site Deploy Plan](./static-site-deploy-plan) | Typed pages and redirects emitting upload metadata, redirect rules, and deploy planning artifacts | | [Docker Compose](./docker-compose) | Typed service definitions, `Env` inputs, dev/prod overrides, `Emit::manifest(...)` | | [GitHub Actions](./github-actions) | Workflow YAML generation, lifting over a node version matrix, `Emit::manifest(...)` | | [GitLab CI](./gitlab-ci) | Pipeline YAML, reusable job templates as functions, rule-based conditions | ## Data generation and multi-environment | Example | What it shows | |---------|--------------| | [Test Fixtures](./test-fixtures) | Faker provider, `range` + `map` to generate N records, deterministic seeds | | [Faker Dataset](./faker-dataset) | Correlated `Person`/`Location` projections, all generator kinds, `refDate` anchoring | | [Multi-env App](./multi-env-app) | `Env` input, base + environment-specific `with` overrides, multi-file modules | | [Feature Flag Matrix](./feature-flag-matrix) | Typed environment and plan combinations emitting a matrix plus per-plan flag bundles | | [Tenant Config Generator](./tenant-config-generator) | One tenant list emitting per-tenant runtime configs and a shared tenant index | ## External data sources | Example | What it shows | |---------|--------------| | [Env Inputs](./env-inputs) | `std::Env` typed schema, field-level `@sensitive`, `Decode` at the env boundary, defaults, `with` | | [Dotenv Config](./dotenv-config) | `File::Read` with `format: dotenv`, boundary `Decode`, typed defaults, and explicit file provenance | | [File-driven Config](./file-driven-config) | `File::Read` YAML loading, typed schema, reshaping with stdlib | | [HTTP Fetch](./http-fetch) | `HTTP::Fetch` with `cacheKey`, typed response schema, transform to manifest | | [HTTP Plus Files Bundle](./http-plus-files-bundle) | Combine a local YAML file and an HTTP fetch into one bundled JSON and YAML output | | [Secrets-Backed Config](./secrets-backed-config) | `aws::SecretsManager::Secret`, `RANK_ENV`, base + secret merge, LocalStack-backed provider flow | | [LocalStack Bootstrap](./localstack-bootstrap) | `Mutation::plan`, `aws::DynamoDB::Put`, `aws::S3::Put`, and LocalStack-backed resource seeding | | [DynamoDB Report](./dynamodb-report) | Typed DynamoDB item reads, collection reductions, and a static JSON report artifact | | [Provider Comparison](./provider-comparison) | Normalize environment, file, HTTP, and S3 inputs into one shared output schema | ## HTTP server | Example | What it shows | |---------|--------------| | [S3 Logs](./s3-logs) | `rank serve`, `aws::S3::Object`, `parse` string patterns, LocalStack-backed log inspection | | [Serve — Docker](./serve-docker) | `rank serve`, `HTTP::Route`, `pub config`, containerized deployment | | [Serve — Lambda](./serve-lambda) | Lambda Web Adapter, `READINESS_CHECK_PATH`, same app code locally and on Lambda | --- ## Hello Config A compact Rank program that still shows validation and composition, not just object literals. ## Source ```rank title="examples/hello-config/main.rank" /// A small but realistic configuration object demonstrating /// type aliases, constraints, and non-destructive overrides with `with`. Mode = `dev` | `prod` AppConfig = Object { name: string, host: string, port: number, debug: bool, } mode: Mode = `dev` @constraint(cond: |self| self >= 1 && self <= 65535) port = mode == `prod` ? 443 : 8080 base_config = { name: `my-app`, host: `localhost`, port: 80, debug: false, } config: AppConfig = base_config with { port: port, debug: mode == `dev`, } pub main = || config ``` ## Output ```json { "name": "my-app", "host": "localhost", "port": 8080, "debug": true } ``` ## Key concepts - **Literal unions** — `Mode = \`dev\` | \`prod\`` keeps configuration modes explicit and finite. - **`@constraint`** — the `port` binding fails fast if the computed value ever falls outside the valid port range. - **`with`** — `base_config with { ... }` expresses overrides without mutating the original object. - **Type annotations** — `config: AppConfig = ...` checks the final composed value against the named output type. ## Run it ```sh rank examples/hello-config ``` --- ## Types and Generics Named type aliases, constrained and defaulted generics, utility types, field annotations on object types, literal unions, and exhaustive `match` expressions. ## Source ```rank title="examples/types-and-generics/main.rank" /// Demonstrates type aliases, constrained and defaulted generics, utility /// types, field annotations on object types, literal unions, and exhaustive /// match expressions. use std::string::{ isBlank } use std::types::{ Partial, Pick } Environment = `dev` | `staging` | `prod` LogLevel = `debug` | `info` | `warn` | `error` Result = Object { ok: bool, data?: T, error?: string, } Field = T[K] ServiceConfig = Object { @constraint(cond: |self| !isBlank(self)) name: string, env: Environment, @constraint(cond: |self| self >= 1) replicas: number, logLevel: LogLevel, } ServiceSummary = Pick ServicePatch = Partial env: Environment = `staging` log_level_for = |e: Environment| -> LogLevel { return match e { `dev` => `debug`, `staging` => `info`, `prod` => `warn`, } } replicas_for = |e: Environment| -> number { return match e { `dev` => 1, `staging` => 2, `prod` => 5, } } config: ServiceConfig = { name: `api`, env: env, replicas: replicas_for(env), logLevel: log_level_for(env), } service_name: Field = config.name replica_count: Field = config.replicas summary: ServiceSummary = { name: service_name, logLevel: config.logLevel, } patch: ServicePatch = { replicas: replica_count, } result: Result = { ok: true, data: config, } pub main = || result ``` ## Output ```json { "ok": true, "data": { "name": "api", "env": "staging", "replicas": 2, "logLevel": "info" } } ``` ## Key concepts - **Union of literals** — `Environment = \`dev\` | \`staging\` | \`prod\`` defines an exact set of allowed values. - **Generic type** — `Result` parameterizes over any type `T`. The compiler infers `T = ServiceConfig` from usage. - **Constrained/defaulted generic alias** — ``Field`` restricts `K` to valid keys and lets `Field` omit the trailing key argument. - **Field annotations on object types** — `ServiceConfig.name` and `ServiceConfig.replicas` carry reusable `@constraint` rules that travel with the type. - **Utility types preserve field annotations** — `Pick` and `Partial` reuse the original field metadata when those fields survive the transform. - **Optional field** — `data?: T` means the field may be absent. When present its type is `T`; when absent it reads as `null`. - **`match` exhaustiveness** — the compiler rejects a `match` that does not cover all variants of a union. ## Run it ```sh rank examples/types-and-generics ``` --- ## Functions and Lifting First-class functions, object and list lifting, and non-destructive override with `with`. ## Source ```rank title="examples/functions-and-lifting/main.rank" /// Demonstrates first-class functions, automatic lifting over lists and objects, /// zipped list lifting, and the `with` patch operator. shift_port = |delta: number, port: number| -> number { return port + delta } scale = |factor: number, value: number| -> number { return value * factor } add = |left: number, right: number| -> number { return left + right } ports = { api: 8080, worker: 9090, admin: 7070, } replica_counts = { api: 3, worker: 2, admin: 1, } base_ports = [8080, 8081, 8082] rollout_offsets = [0, 100, 200] base_service = { port: 3000, replicas: 1, debug: false, } dev_override = base_service with { debug: true, } prod_override = base_service with { replicas: 5, } pub main = || { shifted_ports: shift_port(10000, ports), scaled_replicas: scale(3, replica_counts), canary_ports: add(base_ports, rollout_offsets), dev: dev_override, prod: prod_override, } ``` ## Output ```json { "shifted_ports": { "api": 18080, "worker": 19090, "admin": 17070 }, "scaled_replicas": { "api": 9, "worker": 6, "admin": 3 }, "canary_ports": [8080, 8181, 8282], "dev": { "port": 3000, "replicas": 1, "debug": true }, "prod": { "port": 3000, "replicas": 5, "debug": false } } ``` ## Key concepts - **Object lifting** — `shift_port(10000, ports)` applies the same scalar function to every field of the object. No explicit traversal code is needed. - **Broadcast over structured values** — `scale(3, replica_counts)` keeps the scalar `3` fixed while lifting across the object on the right. - **Zipped list lifting** — `add(base_ports, rollout_offsets)` aligns both lists by position and applies `add` element-by-element. - **`with` patch** — `base_service with { debug: true }` produces a new object identical to `base_service` except for the overridden fields. The original is never mutated. ## Run it ```sh rank examples/functions-and-lifting ``` --- ## Pipes and Collections The pipe operator `|>` with `map`, `filter`, `reduce`, `groupBy`, `sortBy`, and `keyBy`. ## Source ```rank title="examples/pipes-and-collections/main.rank" /// Demonstrates the pipe operator and stdlib collection functions: /// map, filter, reduce, groupBy, sortBy, and keyBy. use std::collections::{ map, filter, reduce, groupBy, sortBy, keyBy } use std::object::{ mapValues } Service = Object { name: string, team: string, port: number, replicas: number, } services: [Service] = [ { name: `api`, team: `backend`, port: 8080, replicas: 3 }, { name: `worker`, team: `backend`, port: 9090, replicas: 2 }, { name: `web`, team: `frontend`, port: 3000, replicas: 4 }, { name: `admin`, team: `frontend`, port: 7070, replicas: 1 }, ] active_services = services |> filter(|svc: Service| svc.replicas > 1) named_services = services |> map(|svc: Service| `${svc.name}:${svc.team}`) total_replicas = services |> reduce(0, |acc: number, svc: Service| acc + svc.replicas) by_team = services |> groupBy(|svc: Service| svc.team) by_name = services |> keyBy(|svc: Service| svc.name) sorted_by_port = services |> sortBy(|svc: Service| svc.port) replica_map = services |> keyBy(|svc: Service| svc.name) |> mapValues(|svc: Service, name: string| svc.replicas) pub main = || { active_services, named_services, total_replicas, by_team, by_name, sorted_by_port, replica_map, } ``` ## Output ```json { "active_services": [ { "name": "api", "team": "backend", "port": 8080, "replicas": 3 }, { "name": "worker", "team": "backend", "port": 9090, "replicas": 2 }, { "name": "web", "team": "frontend", "port": 3000, "replicas": 4 } ], "named_services": ["api:backend", "worker:backend", "web:frontend", "admin:frontend"], "total_replicas": 10, "by_team": { "backend": [ { "name": "api", "team": "backend", "port": 8080, "replicas": 3 }, { "name": "worker", "team": "backend", "port": 9090, "replicas": 2 } ], "frontend": [ { "name": "web", "team": "frontend", "port": 3000, "replicas": 4 }, { "name": "admin", "team": "frontend", "port": 7070, "replicas": 1 } ] }, "sorted_by_port": [ { "name": "web", "team": "frontend", "port": 3000, "replicas": 4 }, { "name": "admin", "team": "frontend", "port": 7070, "replicas": 1 }, { "name": "api", "team": "backend", "port": 8080, "replicas": 3 }, { "name": "worker", "team": "backend", "port": 9090, "replicas": 2 } ], "replica_map": { "api": 3, "worker": 2, "web": 4, "admin": 1 } } ``` ## Key concepts - **Pipe `|>`** — `services |> filter(...)` passes `services` as the first argument to `filter`. Chains read left-to-right. - **`groupBy`** — partitions a list into an object whose keys are the return values of the callback. - **`keyBy`** — like `groupBy` but asserts each key appears exactly once, returning an object of single values. - **`mapValues`** — transforms only the values of an object, leaving keys intact. - **`reduce`** — folds a list to a single value. The accumulator type is inferred from the `initial` argument. ## Run it ```sh rank examples/pipes-and-collections ``` --- ## Docker Compose Generate a `docker-compose.yml` from typed service definitions, reading environment variables for deployment-specific overrides. ## Source ```rank title="examples/docker-compose/types.rank" pub ServiceDef = Object { image: string, ports?: [string], environment: Object { ...: string }, depends_on?: [string], restart: string, } pub NetworkDef = Object { driver: string, } pub VolumeDef = Object { driver: string, } pub Compose = Object { version: string, services: Object { ...: ServiceDef }, networks?: Object { ...: NetworkDef }, volumes?: Object { ...: VolumeDef }, } ``` ```rank title="examples/docker-compose/main.rank" /// Generates a Docker Compose file from typed service definitions. /// Shows Env inputs for environment-specific overrides, the `with` patch /// pattern for dev vs prod, and Emit::manifest to emit a YAML artifact. use std::Env use std::Emit use std::Path use root::types::{ Compose, ServiceDef } SysEnv = Object { COMPOSE_ENV?: string, DB_PASSWORD?: string, ...: string, } env = Env {} compose_env = env.COMPOSE_ENV ?? `dev` db_password = env.DB_PASSWORD ?? `changeme` is_prod = compose_env == `prod` base_api: ServiceDef = { image: `my-org/api:latest`, ports: [`8080:8080`], environment: { NODE_ENV: compose_env, DB_HOST: `db`, DB_PASSWORD: db_password, }, depends_on: [`db`], restart: `on-failure`, } api: ServiceDef = is_prod ? base_api with { restart: `always` } : base_api with { environment: base_api.environment with { DEBUG: `true` } } db: ServiceDef = { image: `postgres:16`, environment: { POSTGRES_PASSWORD: db_password, POSTGRES_DB: `app`, }, restart: `always`, } compose: Compose = { version: `3.9`, services: { api, db }, networks: { default: { driver: `bridge` }, }, volumes: { pgdata: { driver: `local` }, }, } pub main = || Emit::manifest({ entries: [ { path: Path::join([`docker-compose.yml`]), format: `yaml`, value: compose, }, ] }) ``` ## Output ```yaml # docker-compose.yml (with COMPOSE_ENV=dev) version: "3.9" services: api: image: my-org/api:latest ports: - "8080:8080" environment: NODE_ENV: dev DB_HOST: db DB_PASSWORD: changeme DEBUG: "true" depends_on: - db restart: on-failure db: image: postgres:16 environment: POSTGRES_PASSWORD: changeme POSTGRES_DB: app restart: always networks: default: driver: bridge volumes: pgdata: driver: local ``` ## Key concepts - **`std::Env`** — reads environment variables at compile time into a typed schema. Optional fields use `??` for defaults. - **Multi-file modules** — `types.rank` exports shared type definitions; this example uses the canonical absolute form `root::types`. In a normal project source tree, a nearby sibling import can also use `super::types`. - **`with` patch** — `base_api with { restart: \`always\` }` merges without mutating `base_api`. - **`Emit::manifest(...)`** — emits one or more named artifacts. The file path and format are part of the descriptor. ## Run it ```sh COMPOSE_ENV=prod DB_PASSWORD=secret rank examples/docker-compose ``` --- ## Kubernetes App Generate a small Kubernetes application bundle from one typed service definition. This example emits a Deployment, Service, ConfigMap, Ingress, and HorizontalPodAutoscaler as separate YAML files. ## Source ```rank title="examples/kubernetes-app/types.rank" use std::collections::{ length } pub Environment = `dev` | `staging` | `prod` pub LogLevel = `debug` | `info` | `warn` | `error` pub FeatureFlag = `enabled` | `disabled` pub AppRuntimeConfig = Object { APP_ENV: Environment, LOG_LEVEL: LogLevel, FEATURE_LEDGER_EXPORT: FeatureFlag, JVM_OPTS: string, } pub AppSpec = Object { @constraint(cond: |self| length(self) > 0) name: string, @constraint(cond: |self| length(self) > 0) namespace: string, @constraint(cond: |self| length(self) > 0) image: string, @constraint(cond: |self| self >= 1 && self <= 65535) containerPort: number, @constraint(cond: |self| self >= 1 && self <= 65535) servicePort: number, @constraint(cond: |self| length(self) > 0) host: string, @constraint(cond: |self| self >= 1) minReplicas: number, @constraint(cond: |self| self >= 1) maxReplicas: number, @constraint(cond: |self| self >= 1 && self <= 100) cpuTargetUtilization: number, env: AppRuntimeConfig, } pub LabelMap = Object { ...: string, } pub Metadata = Object { name: string, namespace?: string, labels?: LabelMap, annotations?: LabelMap, } pub LabelSelector = Object { matchLabels: LabelMap, } pub ConfigMapEnvSource = Object { configMapRef: Object { name: string, }, } pub ContainerPort = Object { containerPort: number, } pub Container = Object { name: string, image: string, ports: [ContainerPort], envFrom?: [ConfigMapEnvSource], } pub PodSpec = Object { containers: [Container], } pub PodTemplate = Object { metadata: Object { labels: LabelMap, }, spec: PodSpec, } pub DeploymentSpec = Object { replicas: number, selector: LabelSelector, template: PodTemplate, } pub Deployment = Object { apiVersion: `apps/v1`, kind: `Deployment`, metadata: Metadata, spec: DeploymentSpec, } pub ServicePort = Object { port: number, targetPort: number, } pub ServiceSpec = Object { type?: `ClusterIP` | `LoadBalancer`, selector: LabelMap, ports: [ServicePort], } pub Service = Object { apiVersion: `v1`, kind: `Service`, metadata: Metadata, spec: ServiceSpec, } pub ConfigMap = Object { apiVersion: `v1`, kind: `ConfigMap`, metadata: Metadata, data: Object { ...: string, }, } pub IngressServiceBackend = Object { name: string, port: Object { number: number, }, } pub IngressBackend = Object { service: IngressServiceBackend, } pub HTTPIngressPath = Object { path: string, pathType: `Prefix` | `Exact`, backend: IngressBackend, } pub HTTPIngressRule = Object { paths: [HTTPIngressPath], } pub IngressRule = Object { host: string, http: HTTPIngressRule, } pub IngressSpec = Object { ingressClassName: string, rules: [IngressRule], } pub Ingress = Object { apiVersion: `networking.k8s.io/v1`, kind: `Ingress`, metadata: Metadata, spec: IngressSpec, } pub CpuMetricTarget = Object { type: `Utilization`, averageUtilization: number, } pub ResourceMetricSource = Object { name: `cpu`, target: CpuMetricTarget, } pub MetricSpec = Object { type: `Resource`, resource: ResourceMetricSource, } pub ScaleTargetRef = Object { apiVersion: `apps/v1`, kind: `Deployment`, name: string, } pub HorizontalPodAutoscalerSpec = Object { scaleTargetRef: ScaleTargetRef, minReplicas: number, maxReplicas: number, metrics: [MetricSpec], } pub HorizontalPodAutoscaler = Object { apiVersion: `autoscaling/v2`, kind: `HorizontalPodAutoscaler`, metadata: Metadata, spec: HorizontalPodAutoscalerSpec, } ``` ```rank title="examples/kubernetes-app/main.rank" /// Emits a small Kubernetes application bundle from one typed service /// definition. Shows shared types, derived manifests, and Emit::manifest. use std::Emit use std::Path use root::types::{ AppSpec, ConfigMap, Deployment, HorizontalPodAutoscaler, Ingress, LabelMap, Metadata, Service, } app: AppSpec = { name: `payments-api`, namespace: `platform`, image: `ghcr.io/acme/payments-api:1.4.0`, containerPort: 8080, servicePort: 80, host: `payments.example.internal`, minReplicas: 2, maxReplicas: 6, cpuTargetUtilization: 70, env: { APP_ENV: `staging`, LOG_LEVEL: `info`, FEATURE_LEDGER_EXPORT: `enabled`, JVM_OPTS: `-Xms256m -Xmx512m`, }, } labels: LabelMap = { `app.kubernetes.io/name`: app.name, `app.kubernetes.io/component`: `api`, `app.kubernetes.io/part-of`: `payments-platform`, `app.kubernetes.io/managed-by`: `rank`, } config_map_name = `${app.name}-config` service_name = app.name ingress_name = `${app.name}-ingress` hpa_name = `${app.name}-hpa` metadata_for = |name: string| -> Metadata { return { name: name, namespace: app.namespace, labels: labels, } } config_map: ConfigMap = { apiVersion: `v1`, kind: `ConfigMap`, metadata: metadata_for(config_map_name), data: { APP_ENV: app.env.APP_ENV, LOG_LEVEL: app.env.LOG_LEVEL, FEATURE_LEDGER_EXPORT: app.env.FEATURE_LEDGER_EXPORT, JVM_OPTS: app.env.JVM_OPTS, }, } deployment: Deployment = { apiVersion: `apps/v1`, kind: `Deployment`, metadata: metadata_for(app.name), spec: { replicas: app.minReplicas, selector: { matchLabels: labels, }, template: { metadata: { labels: labels, }, spec: { containers: [ { name: app.name, image: app.image, ports: [ { containerPort: app.containerPort, }, ], envFrom: [ { configMapRef: { name: config_map_name, }, }, ], }, ], }, }, }, } service: Service = { apiVersion: `v1`, kind: `Service`, metadata: metadata_for(service_name), spec: { type: `ClusterIP`, selector: labels, ports: [ { port: app.servicePort, targetPort: app.containerPort, }, ], }, } ingress: Ingress = { apiVersion: `networking.k8s.io/v1`, kind: `Ingress`, metadata: metadata_for(ingress_name), spec: { ingressClassName: `nginx`, rules: [ { host: app.host, http: { paths: [ { path: `/`, pathType: `Prefix`, backend: { service: { name: service_name, port: { number: app.servicePort, }, }, }, }, ], }, }, ], }, } hpa: HorizontalPodAutoscaler = { apiVersion: `autoscaling/v2`, kind: `HorizontalPodAutoscaler`, metadata: metadata_for(hpa_name), spec: { scaleTargetRef: { apiVersion: `apps/v1`, kind: `Deployment`, name: app.name, }, minReplicas: app.minReplicas, maxReplicas: app.maxReplicas, metrics: [ { type: `Resource`, resource: { name: `cpu`, target: { type: `Utilization`, averageUtilization: app.cpuTargetUtilization, }, }, }, ], }, } pub main = || Emit::manifest({ entries: [ { path: Path::join([`k8s`, `configmap.yaml`]), format: `yaml`, value: config_map, }, { path: Path::join([`k8s`, `deployment.yaml`]), format: `yaml`, value: deployment, }, { path: Path::join([`k8s`, `service.yaml`]), format: `yaml`, value: service, }, { path: Path::join([`k8s`, `ingress.yaml`]), format: `yaml`, value: ingress, }, { path: Path::join([`k8s`, `hpa.yaml`]), format: `yaml`, value: hpa, }, ] }) ``` ## Output Running the example with `--file-root` writes these files: - `k8s/configmap.yaml` - `k8s/deployment.yaml` - `k8s/service.yaml` - `k8s/ingress.yaml` - `k8s/hpa.yaml` ```yaml # k8s/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: payments-api namespace: platform labels: app.kubernetes.io/name: payments-api app.kubernetes.io/component: api app.kubernetes.io/part-of: payments-platform app.kubernetes.io/managed-by: rank spec: replicas: 2 selector: matchLabels: app.kubernetes.io/name: payments-api app.kubernetes.io/component: api app.kubernetes.io/part-of: payments-platform app.kubernetes.io/managed-by: rank template: metadata: labels: app.kubernetes.io/name: payments-api app.kubernetes.io/component: api app.kubernetes.io/part-of: payments-platform app.kubernetes.io/managed-by: rank spec: containers: - name: payments-api image: ghcr.io/acme/payments-api:1.4.0 ports: - containerPort: 8080 envFrom: - configMapRef: name: payments-api-config ``` ```yaml # k8s/hpa.yaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: payments-api-hpa namespace: platform labels: app.kubernetes.io/name: payments-api app.kubernetes.io/component: api app.kubernetes.io/part-of: payments-platform app.kubernetes.io/managed-by: rank spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: payments-api minReplicas: 2 maxReplicas: 6 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 ``` ## Key concepts - **Single typed app spec** drives every Kubernetes artifact instead of hand-editing multiple YAML files. - **Shared metadata helper** keeps labels and namespace consistent across Deployment, Service, Ingress, ConfigMap, and HPA. - **`Emit::manifest(...)`** emits multiple named files from one Rank program. - **ConfigMap-backed container configuration** keeps runtime settings alongside the workload definition. - **Autoscaling policy as data** makes min/max replicas and CPU targets part of the checked program. ## Run it ```sh rank examples/kubernetes-app --file-root out/kubernetes-app ``` --- ## Service Catalog Define a typed service catalog once and emit multiple downstream artifacts from it: service summary data, a CI matrix, public endpoint metadata, Docker Compose, and deployment metadata. ## Source ```rank title="examples/service-catalog/types.rank" pub Team = `web-platform` | `payments` | `operations` pub Tier = `frontend` | `api` | `worker` pub Exposure = `public` | `internal` pub ServiceDef = Object { name: string, image: string, owner: Team, tier: Tier, exposure: Exposure, buildCommand: string, testCommand: string, port?: number, dependsOn?: [string], healthPath?: string, } pub ComposeService = Object { image: string, ports?: [string], depends_on?: [string], environment: Object { ...: string }, } pub Compose = Object { version: string, services: Object { ...: ComposeService }, } pub ServiceSummary = Object { name: string, owner: Team, tier: Tier, exposure: Exposure, } pub MatrixEntry = Object { service: string, owner: Team, buildCommand: string, testCommand: string, } pub Matrix = Object { include: [MatrixEntry], } pub PublicEndpoint = Object { service: string, url: string, healthPath?: string, } pub DeploymentRecord = Object { service: string, owner: Team, image: string, tier: Tier, dependsOn: [string], } pub DeploymentBundle = Object { environment: string, services: [DeploymentRecord], } ``` ```rank title="examples/service-catalog/main.rank" /// Emits multiple derived artifacts from one typed service catalog. /// Shows shared source-of-truth data, collection transforms, and /// Emit::manifest across JSON and YAML outputs. use std::Emit use std::Path use std::collections::{ filter, map } use root::types::{ Compose, ComposeService, DeploymentBundle, DeploymentRecord, Matrix, MatrixEntry, PublicEndpoint, ServiceDef, ServiceSummary, } services: [ServiceDef] = [ { name: `web`, image: `ghcr.io/acme/web:2.3.0`, owner: `web-platform`, tier: `frontend`, exposure: `public`, buildCommand: `npm run build -w web`, testCommand: `npm test -w web`, port: 3000, dependsOn: [`api`], healthPath: `/healthz`, }, { name: `api`, image: `ghcr.io/acme/api:2.3.0`, owner: `payments`, tier: `api`, exposure: `internal`, buildCommand: `npm run build -w api`, testCommand: `npm test -w api`, port: 8080, dependsOn: [`redis`], healthPath: `/health`, }, { name: `worker`, image: `ghcr.io/acme/worker:2.3.0`, owner: `operations`, tier: `worker`, exposure: `internal`, buildCommand: `npm run build -w worker`, testCommand: `npm test -w worker`, dependsOn: [`api`, `redis`], }, ] web = services[0] api = services[1] worker = services[2] summary_for = |service: ServiceDef| -> ServiceSummary { return { name: service.name, owner: service.owner, tier: service.tier, exposure: service.exposure, } } matrix_entry_for = |service: ServiceDef| -> MatrixEntry { return { service: service.name, owner: service.owner, buildCommand: service.buildCommand, testCommand: service.testCommand, } } deployment_for = |service: ServiceDef| -> DeploymentRecord { return { service: service.name, owner: service.owner, image: service.image, tier: service.tier, dependsOn: service.dependsOn ?? [], } } compose_service_for = |service: ServiceDef| -> ComposeService { port = service.port ?? 0 return { image: service.image, ports: service.port == null ? [] : [`${port}:${port}`], depends_on: service.dependsOn ?? [], environment: { SERVICE_NAME: service.name, SERVICE_TIER: service.tier, OWNER_TEAM: service.owner, }, } } public_endpoint_for = |service: ServiceDef| -> PublicEndpoint { return { service: service.name, url: `https://${service.name}.example.internal`, healthPath: service.healthPath ?? `/health`, } } summary = services |> map(|service: ServiceDef| summary_for(service)) matrix: Matrix = { include: services |> map(|service: ServiceDef| matrix_entry_for(service)), } public_endpoints = services |> filter(|service: ServiceDef| service.exposure == `public`) |> map(|service: ServiceDef| public_endpoint_for(service)) compose: Compose = { version: `3.9`, services: { web: compose_service_for(web), api: compose_service_for(api), worker: compose_service_for(worker), redis: { image: `redis:7`, ports: [`6379:6379`], environment: { SERVICE_NAME: `redis`, SERVICE_TIER: `cache`, OWNER_TEAM: `operations`, }, }, }, } deployment_bundle: DeploymentBundle = { environment: `staging`, services: services |> map(|service: ServiceDef| deployment_for(service)), } pub main = || Emit::manifest({ entries: [ { path: Path::join([`catalog`, `service-summary.json`]), format: `json`, value: summary, }, { path: Path::join([`catalog`, `ci-matrix.json`]), format: `json`, value: matrix, }, { path: Path::join([`catalog`, `public-endpoints.json`]), format: `json`, value: public_endpoints, }, { path: Path::join([`compose`, `docker-compose.yml`]), format: `yaml`, value: compose, }, { path: Path::join([`deploy`, `staging-services.yaml`]), format: `yaml`, value: deployment_bundle, }, ] }) ``` ## Output Running the example with `--file-root` writes: - `catalog/service-summary.json` - `catalog/ci-matrix.json` - `catalog/public-endpoints.json` - `compose/docker-compose.yml` - `deploy/staging-services.yaml` ```json { "include": [ { "service": "web", "owner": "web-platform", "buildCommand": "npm run build -w web", "testCommand": "npm test -w web" }, { "service": "api", "owner": "payments", "buildCommand": "npm run build -w api", "testCommand": "npm test -w api" }, { "service": "worker", "owner": "operations", "buildCommand": "npm run build -w worker", "testCommand": "npm test -w worker" } ] } ``` ## Key concepts - **Single typed catalog** keeps ownership, deployment, and CI metadata in one source of truth. - **Collection transforms** derive summaries, CI inputs, and public endpoint data with `map` and `filter` instead of repeating hand-maintained files. - **Mixed artifact emission** uses one Rank program to generate both JSON and YAML outputs. - **Compose and deployment views** are projections of the same catalog, not separate config trees that can drift. ## Run it ```sh rank examples/service-catalog --file-root out/service-catalog ``` --- ## Static Site Deploy Plan Describe a static site once and emit upload metadata, redirects, and a deploy plan from the same typed source. ## Source ```rank title="examples/static-site-deploy-plan/types.rank" pub Page = Object { path: string, source: string, cachePolicy: `short` | `long`, } pub Redirect = Object { from: string, to: string, status: 301 | 302, } pub UploadEntry = Object { key: string, source: string, cacheControl: string, } ``` ```rank title="examples/static-site-deploy-plan/main.rank" /// Generates a static-site deployment bundle from typed page and redirect data. /// Shows one content model driving routing, caching, and upload planning. use std::Emit use std::Path use std::collections::{ map } use root::types::{ Page, Redirect, UploadEntry } pages: [Page] = [ { path: `/`, source: `dist/index.html`, cachePolicy: `short`, }, { path: `/pricing`, source: `dist/pricing/index.html`, cachePolicy: `short`, }, { path: `/assets/app.js`, source: `dist/assets/app.93d2.js`, cachePolicy: `long`, }, { path: `/assets/styles.css`, source: `dist/assets/styles.a12c.css`, cachePolicy: `long`, }, ] redirects: [Redirect] = [ { from: `/docs`, to: `/pricing`, status: 302, }, { from: `/home`, to: `/`, status: 301, }, ] cache_control_for = |page: Page| -> string { return match page.cachePolicy { `short` => `public, max-age=60, stale-while-revalidate=300`, `long` => `public, max-age=31536000, immutable`, } } upload_entry_for = |page: Page| -> UploadEntry { key = page.path == `/` ? `index.html` : page.path return { key: key, source: page.source, cacheControl: cache_control_for(page), } } upload_manifest = pages |> map(|page: Page| upload_entry_for(page)) deployment_plan = { bucket: `rank-static-site`, invalidatePaths: pages |> map(|page: Page| page.path), uploadCount: 4, } pub main = || Emit::manifest({ entries: [ { path: Path::join([`site`, `uploads.json`]), format: `json`, value: upload_manifest, }, { path: Path::join([`site`, `redirects.json`]), format: `json`, value: redirects, }, { path: Path::join([`site`, `deployment-plan.json`]), format: `json`, value: deployment_plan, }, ] }) ``` ## Output Running the example with `--file-root` writes: - `site/uploads.json` - `site/redirects.json` - `site/deployment-plan.json` ## Run it ```sh npm run rank -- examples/static-site-deploy-plan --file-root out/static-site-deploy-plan ``` ## Key concepts - A typed page list controls cache headers, upload keys, and CDN invalidation paths. - Redirect data stays first-class and emits as its own artifact. - One manifest can describe both file uploads and deployment metadata. --- ## GitHub Actions Generate a CI workflow that tests against multiple Node.js versions using lifting over a version list. ## Source ```rank title="examples/github-actions/main.rank" /// Generates a GitHub Actions CI workflow. /// Shows string template composition, lifting map over a job matrix, /// and conditional step inclusion via ternary expressions. use std::collections::{ map } use std::Emit use std::Path Step = Object { name: string, uses?: string, run?: string, with?: Object { ...: string }, env?: Object { ...: string }, } Job = Object { name: string, `runs-on`: string, steps: [Step], } Workflow = Object { name: string, on: Object { push: Object { branches: [string] }, pull_request: Object { branches: [string] }, }, jobs: Object { ...: Job }, } node_versions: [string] = [`18`, `20`, `22`] checkout_step: Step = { name: `Checkout`, uses: `actions/checkout@v4`, } setup_node = |version: string| -> Step { return { name: `Setup Node ${version}`, uses: `actions/setup-node@v4`, with: { `node-version`: version }, } } install_step: Step = { name: `Install dependencies`, run: `npm ci`, } test_step: Step = { name: `Run tests`, run: `npm test`, } build_job = |version: string| -> Job { return { name: `build-node-${version}`, `runs-on`: `ubuntu-latest`, steps: [ checkout_step, setup_node(version), install_step, test_step, ], } } jobs: [Job] = node_versions |> map(|v: string| build_job(v)) workflow: Workflow = { name: `CI`, on: { push: { branches: [`main`] }, pull_request: { branches: [`main`] }, }, jobs: { `build-18`: jobs[0], `build-20`: jobs[1], `build-22`: jobs[2], }, } pub main = || Emit::manifest({ entries: [ { path: Path::join([`.github`, `workflows`, `ci.yml`]), format: `yaml`, value: workflow, }, ] }) ``` ## Output ```yaml # .github/workflows/ci.yml name: CI on: push: branches: - main pull_request: branches: - main jobs: build-18: name: build-node-18 runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node 18 uses: actions/setup-node@v4 with: node-version: "18" - name: Install dependencies run: npm ci - name: Run tests run: npm test build-20: # ... same pattern for 20 and 22 ``` ## Key concepts - **`map` over a list** — `node_versions |> map(|v| build_job(v))` produces one `Job` per version with no boilerplate. - **String interpolation in keys** — backtick strings like `` `build-node-${version}` `` work in field names and values. - **Quoted field names** — `` `runs-on` `` uses a backtick string as a field name to handle the hyphen. - **`Path::join`** — constructs the nested output path `.github/workflows/ci.yml` safely. ## Run it ```sh rank examples/github-actions ``` --- ## GitLab CI Generate a `.gitlab-ci.yml` pipeline with reusable job templates defined as functions and rule-based conditional execution. ## Source ```rank title="examples/gitlab-ci/main.rank" /// Generates a GitLab CI pipeline configuration. /// Shows reusable job templates defined as functions, stage ordering, /// and rule-based conditional job execution. use std::Emit use std::Path Rule = Object { `if`: string, } Job = Object { stage: string, image: string, script: [string], rules?: [Rule], needs?: [string], artifacts?: Object { paths: [string], expire_in: string, }, } Pipeline = Object { stages: [string], variables: Object { ...: string }, ...: Job, } stages: [string] = [`build`, `test`, `deploy`] mr_only: Rule = { `if`: `$CI_PIPELINE_SOURCE == "merge_request_event"` } main_only: Rule = { `if`: `$CI_COMMIT_BRANCH == "main"` } always: Rule = { `if`: `$CI_PIPELINE_SOURCE != ""` } node_job = |stage: string, script: [string]| -> Job { return { stage: stage, image: `node:20-alpine`, script: script, } } build: Job = node_job(`build`, [`npm ci`, `npm run build`]) with { artifacts: { paths: [`dist/`], expire_in: `1 hour`, }, rules: [always], } test: Job = node_job(`test`, [`npm ci`, `npm test`]) with { rules: [always], needs: [`build`], } deploy_staging: Job = node_job(`deploy`, [`./scripts/deploy.sh staging`]) with { rules: [mr_only], needs: [`test`], } deploy_prod: Job = node_job(`deploy`, [`./scripts/deploy.sh production`]) with { rules: [main_only], needs: [`test`], } pipeline: Pipeline = { stages: stages, variables: { DOCKER_DRIVER: `overlay2`, NODE_ENV: `ci`, }, build: build, test: test, `deploy:staging`: deploy_staging, `deploy:prod`: deploy_prod, } pub main = || Emit::manifest({ entries: [ { path: Path::join([`.gitlab-ci.yml`]), format: `yaml`, value: pipeline, }, ] }) ``` ## Output ```yaml # .gitlab-ci.yml stages: - build - test - deploy variables: DOCKER_DRIVER: overlay2 NODE_ENV: ci build: stage: build image: node:20-alpine script: - npm ci - npm run build artifacts: paths: - dist/ expire_in: 1 hour rules: - if: '$CI_PIPELINE_SOURCE != ""' test: stage: test image: node:20-alpine script: - npm ci - npm test needs: - build rules: - if: '$CI_PIPELINE_SOURCE != ""' deploy:staging: stage: deploy image: node:20-alpine script: - ./scripts/deploy.sh staging needs: - test rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' deploy:prod: stage: deploy image: node:20-alpine script: - ./scripts/deploy.sh production needs: - test rules: - if: '$CI_COMMIT_BRANCH == "main"' ``` ## Key concepts - **Function-as-template** — `node_job` returns a base `Job` that callers customise with `with`, avoiding repetition while keeping each job explicit. - **Open object type** — `Pipeline = Object { stages: ..., ...: Job }` allows arbitrary additional fields (the job entries) beyond the declared ones. - **`with` for optional fields** — adding `artifacts` or `rules` via `with` keeps the base template minimal and each job self-describing. ## Run it ```sh rank examples/gitlab-ci ``` --- ## Test Fixtures Generate a deterministic set of fake user records using the Faker provider and `range` + `map`. ## Source ```rank title="examples/test-fixtures/main.rank" /// Generates deterministic test fixture data using the Faker provider. /// Shows Rand::seed for reproducibility, range + map to produce N records, /// and typed schemas to validate the generated shape. use faker::{ Generate, UUID, Person, Int } use faker::types::{ Spec } use std::list::{ range } use std::collections::{ map } User = Object { id: string, name: string, age: number, } seed = 42 count = 10 spec: Spec = { id: UUID {}, name: Person { sex: `female` }, age: Int { min: 18, max: 65 }, } users: [User] = range(count) |> map(|_, ctx| Generate { spec, seed, itemKey: ctx.index, locale: `en_US`, }) pub main = || { count: count, users: users, } ``` ## Output ```json { "count": 10, "users": [ { "id": "a1b2c3d4-...", "name": "Alice Johnson", "age": 34 }, { "id": "e5f6a7b8-...", "name": "Maria Garcia", "age": 27 }, ... ] } ``` The output is **identical on every run** because `seed` and `itemKey` together fully determine each generated value. ## Key concepts - **`Spec`** — a typed descriptor that pairs each field of `T` with a Faker generator. The compiler checks that every field is covered. - **`Generate`** — evaluates a `Spec` with a seed and item key. Different `itemKey` values produce different records; the same key always produces the same record. - **`range(count) |> map`** — generates indices `[0, 1, ..., count-1]` and maps each to a `Generate` call, passing `ctx.index` as the `itemKey`. - **`ctx` parameter** — the second argument to `map` callbacks is an iterator context with `index`, `size`, `isFirst`, and `isLast`. ## Run it ```sh rank examples/test-fixtures ``` Requires the `faker` provider package. See the [Faker Provider](../faker-provider) docs for installation. --- ## Faker Dataset Generate a deterministic set of user records with correlated person and location fields, using all of the Faker provider's generator kinds. ## Source ```toml title="examples/faker-dataset/rank.toml" manifestVersion = 1 [package] name = "faker-dataset" version = "0.1.0" source = "." [providers] faker = { registry = "npm", package = "@rank-lang/plugin-faker", version = "0.1.0" } ``` ```rank title="examples/faker-dataset/main.rank" /// Generates a deterministic seed dataset of users using the Faker provider. /// Shows correlated Person and Location projections, all generator kinds /// (UUID, Int, Boolean, DateTime, Person, Location), and multi-record /// collection generation with Emit::manifest. use faker::{ Boolean, DateTime, Generate, Int, Location, Person, UUID } use faker::types::{ Spec } use std::Time use std::list::{ range } use std::collections::{ map } use std::Emit use std::Path /// --- Types --- Address = Object { street: string, city: string, country: string, } User = Object { id: string, firstName: string, lastName: string, email: string, age: number, address: Address, role: string, active: bool, createdAt: Time::Instant, score: number, } /// --- Generator descriptors --- seed = 1 refDate: Time::Instant = Time::parse(`2025-01-01T00:00:00Z`, { format: `RFC3339` }) person = Person { sex: `female` } location = Location {} userSpec: Spec = { id: UUID {}, firstName: person.firstName, lastName: person.lastName, email: person.email, age: Int { min: 18, max: 65 }, address: { street: location.street, city: location.city, country: location.country, }, role: `member`, active: Boolean { probability: 0.8 }, createdAt: DateTime { past: { years: 3 }, }, score: Int { min: 0, max: 1000 }, } /// --- Generate 20 users --- count = 20 users: [User] = range(count) |> map(|_, ctx| Generate { spec: userSpec, seed, itemKey: ctx.index, locale: `en_US`, refDate, }) /// --- Emit --- pub main = || Emit::manifest({ entries: [ { path: Path::join([`users.json`]), format: `json`, value: users, }, ] }) ``` ## Output ```json // users.json (first two records shown) [ { "id": "a3f1c2d4-...", "firstName": "Sophia", "lastName": "Martinez", "email": "Sophia.Martinez@example.com", "age": 34, "address": { "street": "742 Evergreen Terrace", "city": "Springfield", "country": "United States" }, "role": "member", "active": true, "createdAt": { "epochMillis": 1672531200000, "offsetMinutes": 0 }, "score": 812 }, { "id": "b7e8d9f0-...", "firstName": "Olivia", "lastName": "Chen", "email": "Olivia.Chen@example.com", "age": 27, "address": { "street": "18 Oak Lane", "city": "Portland", "country": "United States" }, "role": "member", "active": false, "createdAt": { "epochMillis": 1698796800000, "offsetMinutes": 0 }, "score": 441 } ] ``` ## Key concepts - **`Person` projections** — `person.firstName`, `person.lastName`, `person.email`, `person.age`, `person.address` are correlated: they all come from the same generated person, so the email matches the name on every record. - **`Location` projections** — `location.street`, `location.city`, `location.country` are similarly correlated from a single generated location. - **`Spec`** — the spec mirrors the shape of `User`. Literal values (`role: \`member\``) are passed through unchanged; generator descriptors are evaluated per record. - **`seed` + `itemKey`** — `seed` fixes the base RNG; `itemKey: ctx.index` derives a distinct sub-stream per record, so record 0 and record 1 are always different but always the same across runs. - **`refDate`** — anchors relative date generators like `DateTime { past: { years: 3 } }` to a fixed point in time, keeping the dataset stable even as the current date changes. - **`Boolean { probability: 0.8 }`** — approximately 80% of users will have `active: true`. ## Run it ```sh rank examples/faker-dataset --file-root out/ ``` Writes `out/users.json`. Requires the `@rank-lang/plugin-faker` package — see the [Faker Provider](../faker-provider) docs for installation. --- ## Multi-env App Read `RANK_ENV` from the environment and produce an environment-specific configuration by patching a shared base config. ## Source ```rank title="examples/multi-env-app/base.rank" /// Shared base configuration for all environments. pub AppBase = Object { name: string, port: number, replicas: number, logLevel: string, db: Object { host: string, port: number, name: string, poolSize: number, }, } pub base: AppBase = { name: `my-app`, port: 8080, replicas: 1, logLevel: `info`, db: { host: `localhost`, port: 5432, name: `app`, poolSize: 5, }, } ``` ```rank title="examples/multi-env-app/main.rank" /// Reads RANK_ENV from the environment and produces an environment-specific /// config by patching the shared base with per-environment overrides. /// Shows Env inputs, match on a union type, and the `with` override pattern. use std::Env use root::base::{ base, AppBase } SysEnv = Object { RANK_ENV?: `dev` | `staging` | `prod` | null, ...: string, } { RANK_ENV } = Env {} config: AppBase = match RANK_ENV ?? `dev` { `dev` => base with { logLevel: `debug`, db: base.db with { poolSize: 2 }, }, `staging` => base with { replicas: 2, db: base.db with { host: `db.staging.internal`, poolSize: 10, }, }, `prod` => base with { replicas: 5, logLevel: `warn`, db: base.db with { host: `db.prod.internal`, poolSize: 25, }, }, } pub main = || config ``` ## Output ```json // RANK_ENV=prod { "name": "my-app", "port": 8080, "replicas": 5, "logLevel": "warn", "db": { "host": "db.prod.internal", "port": 5432, "name": "app", "poolSize": 25 } } ``` ## Key concepts - **`std::Env`** — reads environment variables into a typed schema. `RANK_ENV` is constrained at the boundary to `` `dev` | `staging` | `prod` | null `` instead of being normalized later from an arbitrary `string`. - **Destructuring env inputs** — `{ RANK_ENV } = Env {}` pulls the field you care about into a local binding while keeping the schema on the `Env` call. - **`match` on a literal union** — `match RANK_ENV ?? `dev`` branches over the closed set of allowed environments with an explicit default when the variable is unset. - **Nested `with`** — `base with { db: base.db with { poolSize: 25 } }` patches deeply without touching unrelated fields. - **Multi-file modules** — `base.rank` exports the shared type and value; this example imports them via the canonical absolute form `root::base`. In a normal project source tree, a nearby sibling import can also use `super::base` when that reads better. ## Run it ```sh RANK_ENV=prod rank examples/multi-env-app RANK_ENV=staging rank examples/multi-env-app rank examples/multi-env-app # defaults to dev ``` --- ## Feature Flag Matrix Generate per-plan feature flag bundles and a flattened environment-by-plan matrix from one typed model. ## Source ```rank title="examples/feature-flag-matrix/types.rank" pub Environment = `dev` | `staging` | `prod` pub Plan = `free` | `pro` | `enterprise` pub FlagState = `enabled` | `disabled` pub FeatureFlags = Object { auditLogs: FlagState, sso: FlagState, ledgerExport: FlagState, prioritySupport: FlagState, } pub FlagMatrixEntry = Object { environment: Environment, plan: Plan, flags: FeatureFlags, } ``` ```rank title="examples/feature-flag-matrix/main.rank" /// Generates per-plan feature flag configs and a flattened matrix view. /// Shows typed environment/plan combinations and multi-file emission. use std::Emit use std::Path use std::collections::{ flatMap, map } use root::types::{ Environment, FeatureFlags, FlagMatrixEntry, Plan } environments: [Environment] = [`dev`, `staging`, `prod`] plans: [Plan] = [`free`, `pro`, `enterprise`] base_flags_for = |plan: Plan| -> FeatureFlags { return match plan { `free` => { auditLogs: `disabled`, sso: `disabled`, ledgerExport: `disabled`, prioritySupport: `disabled`, }, `pro` => { auditLogs: `enabled`, sso: `disabled`, ledgerExport: `enabled`, prioritySupport: `disabled`, }, `enterprise` => { auditLogs: `enabled`, sso: `enabled`, ledgerExport: `enabled`, prioritySupport: `enabled`, }, } } flags_for = |environment: Environment, plan: Plan| -> FeatureFlags { base = base_flags_for(plan) return match environment { `dev` => base with { auditLogs: `enabled`, ledgerExport: `enabled`, }, `staging` => base, `prod` => base, } } entry_for = |environment: Environment, plan: Plan| -> FlagMatrixEntry { return { environment: environment, plan: plan, flags: flags_for(environment, plan), } } matrix_entries = environments |> flatMap(|environment: Environment| plans |> map(|plan: Plan| entry_for(environment, plan))) plan_bundle_for = |plan: Plan| { return { plan: plan, environments: environments |> map(|environment: Environment| { environment: environment, flags: flags_for(environment, plan), }), } } pub main = || Emit::manifest({ entries: [ { path: Path::join([`flags`, `matrix.json`]), format: `json`, value: matrix_entries, }, { path: Path::join([`flags`, `free.json`]), format: `json`, value: plan_bundle_for(`free`), }, { path: Path::join([`flags`, `pro.json`]), format: `json`, value: plan_bundle_for(`pro`), }, { path: Path::join([`flags`, `enterprise.json`]), format: `json`, value: plan_bundle_for(`enterprise`), }, ] }) ``` ## Output Running the example with `--file-root` writes: - `flags/matrix.json` - `flags/free.json` - `flags/pro.json` - `flags/enterprise.json` ## Run it ```sh npm run rank -- examples/feature-flag-matrix --file-root out/feature-flag-matrix ``` ## Key concepts - Typed environment and plan dimensions become a complete matrix with `flatMap`. - A shared feature model emits both per-plan bundles and a flattened aggregate view. - `with` lets one environment patch a base plan config without duplicating the whole object. --- ## Tenant Config Generator Drive per-tenant runtime configs and a shared tenant index from one typed tenant list. ## Source ```rank title="examples/tenant-config-generator/types.rank" pub Plan = `starter` | `growth` | `enterprise` pub Region = `us-east-1` | `eu-west-1` | `ap-southeast-1` pub Theme = `blueprint` | `sand` | `forest` pub Tenant = Object { id: string, displayName: string, plan: Plan, region: Region, domain: string, theme: Theme, } pub TenantConfig = Object { tenantId: string, displayName: string, baseUrl: string, region: Region, plan: Plan, features: Object { sso: bool, auditLogs: bool, customDomain: bool, }, branding: Object { theme: Theme, logoPath: string, }, } ``` ```rank title="examples/tenant-config-generator/main.rank" /// Generates per-tenant runtime configs plus a shared routing index. /// Shows one tenant list driving multiple downstream artifacts. use std::Emit use std::Path use std::collections::{ map } use root::types::{ Plan, Tenant, TenantConfig } tenants: [Tenant] = [ { id: `atlas`, displayName: `Atlas Health`, plan: `starter`, region: `us-east-1`, domain: `atlas.example.com`, theme: `blueprint`, }, { id: `helix`, displayName: `Helix Manufacturing`, plan: `growth`, region: `eu-west-1`, domain: `helix.example.eu`, theme: `sand`, }, { id: `nova`, displayName: `Nova Payments`, plan: `enterprise`, region: `ap-southeast-1`, domain: `nova.example.io`, theme: `forest`, }, ] features_for = |plan: Plan| { return match plan { `starter` => { sso: false, auditLogs: false, customDomain: true, }, `growth` => { sso: true, auditLogs: false, customDomain: true, }, `enterprise` => { sso: true, auditLogs: true, customDomain: true, }, } } config_for = |tenant: Tenant| -> TenantConfig { return { tenantId: tenant.id, displayName: tenant.displayName, baseUrl: `https://${tenant.domain}`, region: tenant.region, plan: tenant.plan, features: features_for(tenant.plan), branding: { theme: tenant.theme, logoPath: `/branding/${tenant.id}.svg`, }, } } tenant_configs = tenants |> map(|tenant: Tenant| config_for(tenant)) index = { tenants: tenant_configs |> map(|config: TenantConfig| { tenantId: config.tenantId, domain: config.baseUrl, region: config.region, plan: config.plan, }), } pub main = || Emit::manifest({ entries: [ { path: Path::join([`tenants`, `atlas.json`]), format: `json`, value: tenant_configs[0], }, { path: Path::join([`tenants`, `helix.json`]), format: `json`, value: tenant_configs[1], }, { path: Path::join([`tenants`, `nova.json`]), format: `json`, value: tenant_configs[2], }, { path: Path::join([`tenants`, `index.json`]), format: `json`, value: index, }, ] }) ``` ## Output Running the example with `--file-root` writes: - `tenants/atlas.json` - `tenants/helix.json` - `tenants/nova.json` - `tenants/index.json` ## Run it ```sh npm run rank -- examples/tenant-config-generator --file-root out/tenant-config-generator ``` ## Key concepts - One typed tenant list drives multiple derived tenant-specific artifacts. - Feature entitlements are computed from plan once and reused across all outputs. - Shared data can emit both leaf artifacts and a cross-tenant routing index. --- ## Env Inputs Read typed environment variables with literal unions, field-level sensitive schema annotations, `Decode` boundary decoding, and environment-aware overrides. ## Source ```rank title="examples/env-inputs/main.rank" /// Shows std::Env with explicit Decode at the env boundary, field-level /// sensitive schema annotations, defaults, and environment-specific overrides. use std::Decode use std::Env AppEnv = `dev` | `staging` | `prod` LogLevel = `debug` | `info` | `warn` | `error` Port = 8080 | 8443 | 9000 SysEnv = Object { APP_ENV: AppEnv, PORT?: Decode, LOG_LEVEL?: LogLevel, @sensitive SECRET_KEY: string, ...: string, } AppConfig = Object { env: AppEnv, port: number, logLevel: LogLevel, debug: bool, baseUrl: string, } env = Env {} port: number = env.PORT ?? 8080 base_config: AppConfig = { env: env.APP_ENV, port: port, logLevel: env.LOG_LEVEL ?? (env.APP_ENV == `prod` ? `warn` : `debug`), debug: env.APP_ENV == `dev`, baseUrl: `http://localhost:${port}`, } config: AppConfig = env.APP_ENV == `prod` ? base_config with { baseUrl: `https://api.example.com` } : base_config pub main = || config ``` ## Output ```json // APP_ENV=prod PORT=8443 LOG_LEVEL=warn SECRET_KEY=abc123 { "env": "prod", "port": 8443, "logLevel": "warn", "debug": false, "baseUrl": "https://api.example.com" } ``` ## Key concepts - **Literal env schema** — `APP_ENV` and `LOG_LEVEL` are constrained to finite unions before the rest of the program runs, and `PORT` is decoded to a numeric union at the boundary. - **Required vs optional vars** — `APP_ENV` and `SECRET_KEY` must be present, while `PORT` and `LOG_LEVEL` can safely fall back. - **Field-level sensitive schema annotation** — `SECRET_KEY` carries `@sensitive` on the type field itself, so the env boundary can redact the secret without needing a separate sensitive binding. - **`Decode` + `??`** — `PORT?: Decode` turns host text into a typed numeric value at the env boundary, and `??` supplies the default when the variable is absent. - **`with` for environment-specific shape** — the production branch overrides only `baseUrl`, leaving the rest of the typed object unchanged. ## Run it ```sh APP_ENV=dev SECRET_KEY=mysecret rank examples/env-inputs APP_ENV=prod PORT=8443 LOG_LEVEL=warn SECRET_KEY=mysecret rank examples/env-inputs ``` --- ## File-driven Config Load a YAML config file at compile time, validate it against a typed schema, and reshape it into a deployment manifest. ## Source ```yaml title="examples/file-driven-config/input.yaml" services: - name: api port: 8080 enabled: true - name: worker port: 9090 enabled: true - name: admin port: 7070 enabled: false database: host: db.internal port: 5432 name: app ``` ```rank title="examples/file-driven-config/main.rank" /// Reads a YAML config file at compile time and reshapes it into a /// deployment manifest. Shows File::Read with a typed schema and /// stdlib transforms on the loaded data. use std::File use std::Path use std::collections::{ filter, map } use std::Emit ServiceEntry = Object { name: string, port: number, enabled: bool, } InputConfig = Object { services: [ServiceEntry], database: Object { host: string, port: number, name: string, }, } DeployedService = Object { name: string, port: number, address: string, } response = File::Read { path: Path::join([`input.yaml`]), format: `yaml`, } input = response.body active_services: [DeployedService] = input.services |> filter(|svc: ServiceEntry| svc.enabled) |> map(|svc: ServiceEntry| -> DeployedService { return { name: svc.name, port: svc.port, address: `${svc.name}.internal:${svc.port}`, } }) manifest = { services: active_services, database: input.database, } pub main = || Emit::manifest({ entries: [ { path: Path::join([`manifest.json`]), format: `json`, value: manifest, }, { path: Path::join([`manifest.yaml`]), format: `yaml`, value: manifest, }, ] }) ``` ## Output ```json // manifest.json { "services": [ { "name": "api", "port": 8080, "address": "api.internal:8080" }, { "name": "worker", "port": 9090, "address": "worker.internal:9090" } ], "database": { "host": "db.internal", "port": 5432, "name": "app" } } ``` The `admin` service is excluded because `enabled: false`. The same manifest is also emitted as `manifest.yaml`. ## Key concepts - **`File::Read`** — reads and parses a file into the typed schema `T`. A schema mismatch is a compile-time error. - **`response.body`** — the parsed, type-checked content of the file. - **`response.ctx`** — metadata: `{ path, format }`. - **`Path::join`** — constructs a `Path::RelativePath` from path segments. Paths are resolved relative to the project source root. - **`Emit::manifest(...)`** — emits both JSON and YAML artifacts from the same typed manifest descriptor. - **Filter then map** — the pipe chain keeps only enabled services and builds the `address` field, leaving the database block untouched. ## Run it ```sh rank examples/file-driven-config ``` --- ## HTTP Fetch Fetch external data from a real API at compile time, validate the response against a typed schema, and transform it into a smaller artifact. ## Source ```rank title="examples/http-fetch/main.rank" /// Fetches a real Lorem API article at compile time and transforms /// the response into a smaller summary artifact. /// Shows HTTP::Fetch with a typed response schema, cacheKey for determinism, /// and accessing both response.body and response.ctx metadata. use std::HTTP use std::Emit use std::Path Author = Object { id: string, name: string, avatar: string, email: string, } Article = Object { slug: string, title: string, subtitle: string, image: string, author: Author, content: string, dateCreated: string, } ArticleSummary = Object { slug: string, title: string, subtitle: string, author: Object { name: string, email: string, }, coverImage: string, publishedAt: string, status: number, source: string, } response = HTTP::Fetch
{ url: `https://lorem-api.com/api/article/rank-http-fetch`, method: `GET`, headers: { Accept: `application/json`, }, cacheKey: `lorem-article-rank-http-fetch-v1`, } article = response.body summary: ArticleSummary = { slug: article.slug, title: article.title, subtitle: article.subtitle, author: { name: article.author.name, email: article.author.email, }, coverImage: article.image, publishedAt: article.dateCreated, status: response.ctx.status, source: response.ctx.cacheKey, } pub main = || Emit::manifest({ entries: [ { path: Path::join([`article-summary.json`]), format: `json`, value: summary, }, ] }) ``` ## Output ```json // article-summary.json { "slug": "rank-http-fetch", "title": "Adeptio abeo cervus voluptatum delego sequi ducimus virga.", "subtitle": "Rerum atqui rerum et.", "author": { "name": "Shannon McDermott V", "email": "Fae.Gerhold@hotmail.com" }, "coverImage": "https://picsum.photos/seed/RA7K2McSOQ/3074/340?blur=1", "publishedAt": "2020-10-01T08:13:16.355Z", "status": 200, "source": "lorem-article-rank-http-fetch-v1" } ``` ## Key concepts - **`HTTP::Fetch`** — issues an HTTP request and validates the parsed response body against `T`. A schema mismatch is a compile-time error. - **Real endpoint** — this example uses Lorem API's article endpoint at `https://lorem-api.com/api/article/{slug}` rather than a placeholder host. - **`cacheKey`** — required for determinism. Rank stores the fetched response under this key; the same key always returns the same data within a build. - **`response.body`** — the type-checked response body. - **`response.ctx`** — request metadata: `{ status, cacheKey, headers }`. - **Compile-time fetch** — the request runs when Rank evaluates the program, not at application startup. The result is baked into the generated artifact. ## Run it ```sh rank examples/http-fetch/main.rank --http-cache examples/http-fetch/http-cache.json rank examples/http-fetch/main.rank --http-cache examples/http-fetch/http-cache.json --file-root out/http-fetch ``` :::note This example includes a checked-in snapshot at `examples/http-fetch/http-cache.json`. The current CLI evaluates `HTTP::Fetch` from snapshot data, so use `--http-cache` to replay the real Lorem API response deterministically in local runs and CI. ::: --- ## HTTP Plus Files Bundle Combine a local YAML config file with an HTTP fetch and emit both a JSON bundle and route metadata. ## Source ```yaml title="examples/http-plus-files-bundle/input.yaml" site: name: Rank Launchpad themeColor: tide navItems: - Overview - Docs - Pricing landing: heroTitle: Ship configuration as code featuredArticleSlug: rank-http-fetch ``` ```rank title="examples/http-plus-files-bundle/main.rank" /// Combines a local YAML config file with an HTTP fetch and emits a bundle. /// Shows File::Read and HTTP::Fetch working together in one program. use std::Emit use std::File use std::HTTP use std::Path InputConfig = Object { site: Object { name: string, themeColor: string, navItems: [string], }, landing: Object { heroTitle: string, featuredArticleSlug: string, }, } Author = Object { id: string, name: string, avatar: string, email: string, } Article = Object { slug: string, title: string, subtitle: string, image: string, author: Author, dateCreated: string, content: string, } input_result = File::Read { path: Path::join([`input.yaml`]), format: `yaml`, } input = input_result.body article_result = HTTP::Fetch
{ url: `https://lorem-api.com/api/article/rank-http-fetch`, method: `GET`, headers: { Accept: `application/json`, }, cacheKey: `http-plus-files-bundle-rank-http-fetch-v1`, } article = article_result.body bundle = { site: { name: input.site.name, themeColor: input.site.themeColor, heroTitle: input.landing.heroTitle, navItems: input.site.navItems, }, featuredArticle: { slug: article.slug, title: article.title, subtitle: article.subtitle, author: article.author.name, publishedAt: article.dateCreated, }, source: { articleCacheKey: article_result.ctx.cacheKey, status: article_result.ctx.status, }, } routes = { siteName: input.site.name, routes: [ { path: `/`, title: input.landing.heroTitle, }, { path: `/articles/${article.slug}`, title: article.title, }, ], } pub main = || Emit::manifest({ entries: [ { path: Path::join([`bundle`, `site-bundle.json`]), format: `json`, value: bundle, }, { path: Path::join([`bundle`, `routes.yaml`]), format: `yaml`, value: routes, }, ] }) ``` ## Output Running the example with `--file-root` writes: - `bundle/site-bundle.json` - `bundle/routes.yaml` ## Run it ```sh npm run rank -- examples/http-plus-files-bundle --file-root out/http-plus-files-bundle ``` ## Key concepts - `File::Read` can load local structured input while `HTTP::Fetch` contributes remote typed data in the same program. - `cacheKey` keeps the HTTP side deterministic for repeated evaluations. - Response metadata from `response.ctx` can flow directly into emitted artifacts. --- ## Secrets-Backed Config Resolve an application config from a shared base object, the selected `RANK_ENV`, and a JSON secret fetched from AWS Secrets Manager. This example is backed by LocalStack so the provider call is runnable in the repository without a real AWS account. ## Source ```rank title="examples/secrets-backed-config/types.rank" pub Environment = `dev` | `staging` | `prod` pub LogLevel = `debug` | `info` | `warn` pub BaseConfig = Object { service: Object { name: string, baseUrl: string, logLevel: LogLevel, }, database: Object { host: string, port: number, name: string, user: string, }, auth: Object { issuer: string, }, integrations: Object { billingApiBaseUrl: string, }, } pub RuntimeSecrets = Object { dbPassword: string, jwtSigningSecret: string, stripeApiKey: string, } pub ResolvedConfig = Object { service: Object { name: string, env: Environment, baseUrl: string, logLevel: LogLevel, }, database: Object { host: string, port: number, name: string, user: string, password: string, }, auth: Object { issuer: string, jwtSigningSecret: string, }, integrations: Object { billingApiBaseUrl: string, stripeApiKey: string, }, source: Object { secretId: string, }, } ``` ```rank title="examples/secrets-backed-config/main.rank" /// Resolves a deployable app config from a shared base, the selected /// environment, and a JSON secret fetched from AWS Secrets Manager. use aws::{ SecretsManager } use std::Env use root::types::{ BaseConfig, Environment, LogLevel, ResolvedConfig, RuntimeSecrets } SysEnv = Object { RANK_ENV?: Environment, SECRETS_ENDPOINT?: string, ...: string, } { RANK_ENV, SECRETS_ENDPOINT } = Env {} selected_env: Environment = RANK_ENV ?? `staging` secrets_endpoint = SECRETS_ENDPOINT ?? `http://127.0.0.1:4566` base: BaseConfig = { service: { name: `payments-api`, baseUrl: `https://payments.internal`, logLevel: `info`, }, database: { host: `db.internal`, port: 5432, name: `payments`, user: `payments_app`, }, auth: { issuer: `https://auth.internal/issuer`, }, integrations: { billingApiBaseUrl: `https://billing.internal/api`, }, } log_level_for = |env: Environment| -> LogLevel { return match env { `dev` => `debug`, `staging` => `info`, `prod` => `warn`, } } secret_id_for = |env: Environment| -> string { return match env { `dev` => `rank/examples/payments-api/dev`, `staging` => `rank/examples/payments-api/staging`, `prod` => `rank/examples/payments-api/prod`, } } env_config: BaseConfig = match selected_env { `dev` => base with { service: base.service with { baseUrl: `https://payments.dev.internal`, logLevel: log_level_for(selected_env), }, database: base.database with { host: `dev-db.internal`, }, integrations: base.integrations with { billingApiBaseUrl: `https://billing.dev.internal/api`, }, }, `staging` => base with { service: base.service with { baseUrl: `https://payments.staging.internal`, logLevel: log_level_for(selected_env), }, database: base.database with { host: `staging-db.internal`, }, integrations: base.integrations with { billingApiBaseUrl: `https://billing.staging.internal/api`, }, }, `prod` => base with { service: base.service with { baseUrl: `https://payments.example.com`, logLevel: log_level_for(selected_env), }, database: base.database with { host: `payments-db.internal`, }, integrations: base.integrations with { billingApiBaseUrl: `https://billing.example.com/api`, }, }, } secrets_client: aws::SecretsManager::ClientConfig = SecretsManager::createClient({ region: `us-east-1`, endpoint: secrets_endpoint, credentials: { accessKeyId: `test`, secretAccessKey: `test`, }, }) secret_id = secret_id_for(selected_env) secret_result: aws::SecretsManager::SecretResult = aws::SecretsManager::Secret { client: secrets_client, secretId: secret_id, format: `json`, } pub main = || -> ResolvedConfig { return { service: env_config.service with { env: selected_env, }, database: env_config.database with { password: secret_result.secret.dbPassword, }, auth: env_config.auth with { jwtSigningSecret: secret_result.secret.jwtSigningSecret, }, integrations: env_config.integrations with { stripeApiKey: secret_result.secret.stripeApiKey, }, source: { secretId: secret_id, }, } } ``` ```toml title="examples/secrets-backed-config/rank.toml" manifestVersion = 1 [package] name = "secrets-backed-config-example" version = "0.1.0" source = "." [providers] aws = { path = "../../packages/plugins/aws" } [security] allow-provider-capabilities = ["network"] allow-env = ["RANK_ENV", "SECRETS_ENDPOINT"] ``` ## Output Default output with `RANK_ENV` unset: ```yaml service: name: payments-api baseUrl: https://payments.staging.internal logLevel: info env: staging database: host: staging-db.internal port: 5432 name: payments user: payments_app password: staging-db-password auth: issuer: https://auth.internal/issuer jwtSigningSecret: staging-jwt-signing-secret integrations: billingApiBaseUrl: https://billing.staging.internal/api stripeApiKey: sk_test_staging_456 source: secretId: rank/examples/payments-api/staging ``` ## Run it Start LocalStack from the example directory: ```sh cd examples/secrets-backed-config docker compose up ``` Then run the Rank program from the workspace root: ```sh npm run rank -- examples/secrets-backed-config ``` Switch environments with `RANK_ENV`: ```sh RANK_ENV=prod npm run rank -- examples/secrets-backed-config ``` If you changed the LocalStack host port, pass the matching endpoint: ```sh RANK_ENV=prod SECRETS_ENDPOINT=http://127.0.0.1:4567 npm run rank -- examples/secrets-backed-config ``` ## Key concepts - **`aws::SecretsManager::Secret`** fetches a typed secret payload and decodes JSON directly into a checked Rank object. - **Base + environment patching** uses `with` to keep the stable config shape separate from environment-specific overrides. - **`std::Env` selection** turns `RANK_ENV` into typed branching instead of ad hoc string checks. - **LocalStack-backed provider example** keeps the example runnable in-repo while still exercising the real provider surface. --- ## LocalStack Bootstrap Create empty LocalStack-backed AWS resources first, then use one Rank mutation plan to seed DynamoDB and S3 together. ## Source ```rank title="examples/localstack-bootstrap/types.rank" use aws::DynamoDB::{ Table } pub SeedRow = Object { dataset: string, id: string, service: string, owner: string, enabled: bool, } pub SeedsTable = Table pub BootstrapBundle = Object { generatedBy: string, generatedAt: string, services: [Object { service: string, owner: string, enabled: bool, }], } ``` ```rank title="examples/localstack-bootstrap/main.rank" /// Seeds LocalStack-backed DynamoDB and S3 resources in one mutation plan. /// Shows Mutation::plan, Mutation::commit, and provider mutation effects. use aws::{ DynamoDB, S3 } use std::Env use std::Mutation use root::types::{ BootstrapBundle, SeedRow, SeedsTable } SysEnv = Object { DYNAMODB_ENDPOINT?: string, S3_ENDPOINT?: string, ...: string, } { DYNAMODB_ENDPOINT, S3_ENDPOINT } = Env {} dynamodb_endpoint = DYNAMODB_ENDPOINT ?? `http://127.0.0.1:4566` s3_endpoint = S3_ENDPOINT ?? `http://127.0.0.1:4566` table_name = `BootstrapSeeds` bucket_name = `rank-bootstrap-seeds` bundle_key = `bootstrap/services.json` dynamo_client: aws::DynamoDB::ClientConfig = DynamoDB::createClient({ region: `us-east-1`, endpoint: dynamodb_endpoint, credentials: { accessKeyId: `test`, secretAccessKey: `test`, }, }) s3_client: aws::S3::ClientConfig = S3::createClient({ region: `us-east-1`, endpoint: s3_endpoint, credentials: { accessKeyId: `test`, secretAccessKey: `test`, }, }) put_web_seed_row: Mutation::Effect = aws::DynamoDB::Put { client: dynamo_client, tableName: table_name, } put_api_seed_row: Mutation::Effect = aws::DynamoDB::Put { client: dynamo_client, tableName: table_name, } put_bundle_seed: Mutation::Effect = aws::S3::Put { client: s3_client, bucket: bucket_name, key: bundle_key, } web_seed: SeedRow = { dataset: `services`, id: `web`, service: `web`, owner: `web-platform`, enabled: true, } api_seed: SeedRow = { dataset: `services`, id: `api`, service: `api`, owner: `payments`, enabled: true, } bundle_seed: BootstrapBundle = { generatedBy: `localstack-bootstrap`, generatedAt: `2026-05-13`, services: [ { service: web_seed.service, owner: web_seed.owner, enabled: web_seed.enabled, }, { service: api_seed.service, owner: api_seed.owner, enabled: api_seed.enabled, }, ], } pub main = || Mutation::commit( Mutation::plan({ webSeed: put_web_seed_row(web_seed), apiSeed: put_api_seed_row(api_seed), bundleSeed: put_bundle_seed(bundle_seed), }), |report| { commitStatus: report.status, targets: { tableName: table_name, bucket: bucket_name, key: bundle_key, }, operations: report.operations, }, ) ``` ```toml title="examples/localstack-bootstrap/rank.toml" manifestVersion = 1 [package] name = "localstack-bootstrap-example" version = "0.1.0" source = "." [providers] aws = { path = "../../packages/plugins/aws" } [security] allow-provider-capabilities = ["network"] allow-provider-mutation-exports = ["aws::DynamoDB::Put", "aws::S3::Put"] allow-env = ["DYNAMODB_ENDPOINT", "S3_ENDPOINT"] ``` ## Output ```yaml commitStatus: succeeded targets: tableName: BootstrapSeeds bucket: rank-bootstrap-seeds key: bootstrap/services.json operations: webSeed: status: succeeded apiSeed: status: succeeded bundleSeed: status: succeeded ``` ## Run it Start LocalStack from the example directory: ```sh cd examples/localstack-bootstrap docker compose up ``` Then run the Rank program from the workspace root: ```sh npm run rank -- examples/localstack-bootstrap ``` If you changed the LocalStack host port, pass matching endpoints: ```sh DYNAMODB_ENDPOINT=http://127.0.0.1:4567 S3_ENDPOINT=http://127.0.0.1:4567 npm run rank -- examples/localstack-bootstrap ``` ## Key concepts - **`Mutation::plan(...)`** groups multiple provider writes into one commit boundary. - **Direct effect bindings** keep the mutation graph explicit: each plan field calls a named local `Mutation::Effect` binding. - **Mixed AWS mutations** seed DynamoDB rows and an S3 object in the same commit. - **LocalStack-backed workflow** keeps the example runnable without touching real AWS resources. --- ## DynamoDB Report Read seeded DynamoDB order items through the AWS provider and emit a static weekly sales report as JSON. ## Source ```rank title="examples/dynamodb-report/types.rank" use aws::DynamoDB::{ Table } pub OrderStatus = `paid` | `pending` | `refunded` pub Region = `us` | `eu` | `apac` pub Order = Object { reportId: string, orderId: string, customer: string, region: Region, status: OrderStatus, total: number, } pub OrdersTable = Table pub RegionSummary = Object { region: Region, orders: number, revenue: number, } pub WeeklyReport = Object { reportId: string, totals: Object { totalOrders: number, grossRevenue: number, paidOrders: number, pendingOrders: number, refundedOrders: number, }, byRegion: [RegionSummary], orders: [Order], } ``` ```rank title="examples/dynamodb-report/main.rank" /// Reads seeded DynamoDB order items and emits a static report artifact. /// Shows provider-backed reads plus pure collection transforms. use aws::{ DynamoDB } use std::Env use std::Emit use std::Path use std::collections::{ count, filter, length, map, reduce } use root::types::{ Order, OrdersTable, Region, RegionSummary, WeeklyReport } SysEnv = Object { DYNAMODB_ENDPOINT?: string, ...: string, } { DYNAMODB_ENDPOINT } = Env {} dynamodb_endpoint = DYNAMODB_ENDPOINT ?? `http://127.0.0.1:4566` report_id = `weekly-2026-05-13` dynamo_client: aws::DynamoDB::ClientConfig = DynamoDB::createClient({ region: `us-east-1`, endpoint: dynamodb_endpoint, credentials: { accessKeyId: `test`, secretAccessKey: `test`, }, }) order_defaults: [Order] = [ { reportId: report_id, orderId: `ord-1001`, customer: `Acme Co`, region: `us`, status: `paid`, total: 240, }, { reportId: report_id, orderId: `ord-1002`, customer: `Globex`, region: `eu`, status: `paid`, total: 180, }, { reportId: report_id, orderId: `ord-1003`, customer: `Initech`, region: `apac`, status: `pending`, total: 95, }, { reportId: report_id, orderId: `ord-1004`, customer: `Umbrella`, region: `us`, status: `refunded`, total: -45, }, ] resolve_order = |fallback: Order| -> Order { result: aws::DynamoDB::ItemResult = aws::DynamoDB::Item { client: dynamo_client, tableName: `Orders`, key: { reportId: fallback.reportId, orderId: fallback.orderId, }, } return result.item ?? fallback } orders: [Order] = order_defaults |> map(|fallback: Order| resolve_order(fallback)) revenue_for = |region: Region| -> number { return orders |> filter(|order: Order| order.region == region) |> reduce(0, |acc: number, order: Order| acc + order.total) } region_summary_for = |region: Region| -> RegionSummary { return { region: region, orders: count(orders, |order: Order| order.region == region), revenue: revenue_for(region), } } report: WeeklyReport = { reportId: report_id, totals: { totalOrders: length(orders), grossRevenue: orders |> reduce(0, |acc: number, order: Order| acc + order.total), paidOrders: count(orders, |order: Order| order.status == `paid`), pendingOrders: count(orders, |order: Order| order.status == `pending`), refundedOrders: count(orders, |order: Order| order.status == `refunded`), }, byRegion: [ region_summary_for(`us`), region_summary_for(`eu`), region_summary_for(`apac`), ], orders: orders, } pub main = || Emit::manifest({ entries: [ { path: Path::join([`reports`, `weekly-sales.json`]), format: `json`, value: report, }, ] }) ``` ## Output ```json { "reportId": "weekly-2026-05-13", "totals": { "totalOrders": 4, "grossRevenue": 470, "paidOrders": 2, "pendingOrders": 1, "refundedOrders": 1 }, "byRegion": [ { "region": "us", "orders": 2, "revenue": 195 }, { "region": "eu", "orders": 1, "revenue": 180 }, { "region": "apac", "orders": 1, "revenue": 95 } ] } ``` ## Run it Start LocalStack from the example directory: ```sh cd examples/dynamodb-report docker compose up ``` Then run the Rank program from the workspace root: ```sh npm run rank -- examples/dynamodb-report --file-root out/dynamodb-report ``` ## Key concepts - **`aws::DynamoDB::Item`** provides typed item reads from seeded DynamoDB data. - **Pure collection transforms** turn fetched items into a static report with `count`, `filter`, `length`, and `reduce`. - **Manifest emission** writes the derived report as a named JSON artifact. - **LocalStack-backed data source** keeps the example runnable without touching real AWS infrastructure. --- ## Provider Comparison Normalize environment variables, a local file, an HTTP response, and a seeded S3 object into one shared output schema. ## Source ```yaml title="examples/provider-comparison/input.yaml" id: file-001 label: File Snapshot owner: docs-team ``` ```rank title="examples/provider-comparison/main.rank" /// Normalizes environment, file, HTTP, and S3 inputs into one schema. /// Shows multiple input surfaces converging on one output bundle. use aws::{ S3 } use std::Emit use std::Env use std::File use std::HTTP use std::Path use std::collections::{ length, map } SourceRecord = Object { id: string, label: string, owner: string, } NormalizedRecord = Object { source: `env` | `file` | `http` | `s3`, id: string, label: string, owner: string, } ArticleAuthor = Object { id: string, name: string, avatar: string, email: string, } Article = Object { slug: string, title: string, subtitle: string, image: string, author: ArticleAuthor, dateCreated: string, content: string, } SysEnv = Object { PRODUCT_ID?: string, PRODUCT_LABEL?: string, PRODUCT_OWNER?: string, S3_ENDPOINT?: string, ...: string, } { PRODUCT_ID, PRODUCT_LABEL, PRODUCT_OWNER, S3_ENDPOINT } = Env {} env_record: NormalizedRecord = { source: `env`, id: PRODUCT_ID ?? `env-001`, label: PRODUCT_LABEL ?? `Environment Snapshot`, owner: PRODUCT_OWNER ?? `platform-runtime`, } file_result = File::Read { path: Path::join([`input.yaml`]), format: `yaml`, } file_record: NormalizedRecord = { source: `file`, id: file_result.body.id, label: file_result.body.label, owner: file_result.body.owner, } http_result = HTTP::Fetch
{ url: `https://lorem-api.com/api/article/rank-http-fetch`, method: `GET`, headers: { Accept: `application/json`, }, cacheKey: `provider-comparison-rank-http-fetch-v1`, } http_record: NormalizedRecord = { source: `http`, id: http_result.body.slug, label: http_result.body.title, owner: http_result.body.author.email, } s3_endpoint = S3_ENDPOINT ?? `http://127.0.0.1:4566` s3_client: aws::S3::ClientConfig = S3::createClient({ region: `us-east-1`, endpoint: s3_endpoint, credentials: { accessKeyId: `test`, secretAccessKey: `test`, }, }) s3_result: aws::S3::ObjectResult = aws::S3::Object { client: s3_client, bucket: `rank-provider-comparison`, key: `profiles/source.json`, } s3_record: NormalizedRecord = { source: `s3`, id: s3_result.object.id, label: s3_result.object.label, owner: s3_result.object.owner, } records: [NormalizedRecord] = [env_record, file_record, http_record, s3_record] summary = { totalSources: length(records), sources: records |> map(|record: NormalizedRecord| record.source), } pub main = || Emit::manifest({ entries: [ { path: Path::join([`comparison`, `normalized.json`]), format: `json`, value: records, }, { path: Path::join([`comparison`, `summary.yaml`]), format: `yaml`, value: summary, }, ] }) ``` ## Output ```yaml totalSources: 4 sources: - env - file - http - s3 ``` ## Run it Start LocalStack from the example directory: ```sh cd examples/provider-comparison docker compose up ``` Then run the Rank program from the workspace root: ```sh npm run rank -- examples/provider-comparison --file-root out/provider-comparison ``` Optionally override the environment-backed record: ```sh PRODUCT_LABEL="Env Override" npm run rank -- examples/provider-comparison --file-root out/provider-comparison ``` ## Key concepts - `Env`, `File::Read`, `HTTP::Fetch`, and `aws::S3::Object` can all normalize into one shared schema. - The S3 input stays local and reproducible by seeding LocalStack in `docker-compose.yml`. - A single emitted bundle makes cross-provider comparisons straightforward to inspect. --- ## S3 Logs Read mock nginx access logs from S3 through the AWS provider, parse each line with `parse` string patterns, and expose the result through `rank serve`. ## Source ```rank title="examples/s3-logs/main.rank" use aws::{ S3 } use std::Env use std::HTTP use std::Runtime use std::collections::{ count, filter, flatMap, length, map } use std::string::{ isBlank, startsWith, split, trim } LogDay = `2026-05-11` | `2026-05-12` StatusClass = `2xx` | `3xx` | `4xx` | `5xx` | `other` AccessLogFile = Object { format: `nginx-access`, server: string, day: LogDay, raw: string, } AccessLogEntry = Object { raw: string, clientIp: string, timestamp: string, method: string, target: string, protocol: string, status: string, statusClass: StatusClass, bytesSent: string, referrer: string, userAgent: string, } SysEnv = Object { S3_ENDPOINT?: string, ...: string, } HealthRoute = HTTP::Route { method: `GET`, path: `/health` } LogsRoute = HTTP::Route { method: `GET`, path: `/logs`, query: Object { day?: LogDay, } } Routes = HealthRoute | LogsRoute log_bucket = `rank-example-logs` default_log_day: LogDay = `2026-05-12` available_log_days: [LogDay] = [`2026-05-11`, `2026-05-12`] { S3_ENDPOINT } = Env {} s3_endpoint = S3_ENDPOINT ?? `http://127.0.0.1:4566` s3Client: aws::S3::ClientConfig = S3::createClient({ region: `us-east-1`, endpoint: s3_endpoint, credentials: { accessKeyId: `test`, secretAccessKey: `test`, }, }) status_class_for = |status: string| -> StatusClass { return startsWith(status, `2`) ? `2xx` : startsWith(status, `3`) ? `3xx` : startsWith(status, `4`) ? `4xx` : startsWith(status, `5`) ? `5xx` : `other` } log_key_for = |day: LogDay| -> string { return match day { `2026-05-11` => `nginx/2026-05-11/access.json`, `2026-05-12` => `nginx/2026-05-12/access.json`, } } parse_access_entries = |line: string| -> [AccessLogEntry] { return match line { parse`{clientIp:string} - {_remoteUser:string} [{timestamp:string}] "{method:string} {target:string} HTTP/{protocol:string}" {status:string} {bytesSent:string} "{referrer:string}" "{userAgent:string}"` => [{ raw: line, clientIp, timestamp, method, target, protocol, status, statusClass: status_class_for(status), bytesSent, referrer, userAgent, }], _ => [], } } list_logs = |day: LogDay| -> HTTP::Response { key = log_key_for(day) fetched: aws::S3::ObjectResult = aws::S3::Object { client: s3Client, bucket: log_bucket, key: key, } { object, metadata } = fetched non_blank_lines = object.raw |> split(`\n`) |> map(|line: string| trim(line)) |> filter(|line: string| !isBlank(line)) entries = non_blank_lines |> flatMap(|line: string| parse_access_entries(line)) return { status: 200, body: { availableDays: available_log_days, source: { bucket: log_bucket, key: key, server: object.server, day: object.day, requestId: metadata.requestId, }, summary: { totalLines: length(non_blank_lines), parsedLines: length(entries), droppedLines: length(non_blank_lines) - length(entries), okResponses: count(entries, |entry: AccessLogEntry| entry.statusClass == `2xx`), redirects: count(entries, |entry: AccessLogEntry| entry.statusClass == `3xx`), clientErrors: count(entries, |entry: AccessLogEntry| entry.statusClass == `4xx`), serverErrors: count(entries, |entry: AccessLogEntry| entry.statusClass == `5xx`), }, entries: entries, } } } pub config = { defaultResponseFormat: `json` } pub main = |req: Runtime::ExecutionContext| -> HTTP::Response { return match req { HealthRoute => { status: 200, body: { ok: true, service: `s3-logs-example`, }, }, LogsRoute => list_logs(req.query.day ?? default_log_day), } } ``` ```toml title="examples/s3-logs/rank.toml" manifestVersion = 1 [package] name = "s3-logs-example" version = "0.1.0" source = "." [providers] aws = { path = "../../packages/plugins/aws" } [security] allow-provider-capabilities = ["network"] allow-env = ["S3_ENDPOINT"] ``` ## Output ```json { "availableDays": ["2026-05-11", "2026-05-12"], "source": { "bucket": "rank-example-logs", "key": "nginx/2026-05-12/access.json", "server": "edge-2", "day": "2026-05-12" }, "summary": { "totalLines": 5, "parsedLines": 5, "droppedLines": 0, "okResponses": 1, "redirects": 0, "clientErrors": 2, "serverErrors": 2 } } ``` ## Key concepts - **`aws::S3::Object`** — reads the seeded object from S3 and validates the wrapper shape before any line parsing runs. - **`parse` string patterns** — the log parser uses a single structured-text `match` arm instead of separate regex capture indexing. - **Pipe + `flatMap`** — the raw text is split, trimmed, filtered, and parsed through one left-to-right collection flow. - **`Runtime::ExecutionContext`** — `GET /health` and `GET /logs` share one route union with typed query narrowing for `day`. - **Current S3 surface** — the current AWS provider reads S3 objects as JSON only, so the seeded raw nginx text lives inside the `raw` field of a JSON object. ## Run it Start LocalStack from `examples/s3-logs`: ```sh docker compose up ``` If another LocalStack setup is already using port `4566`, choose a different host port: ```sh LOCALSTACK_PORT=4567 docker compose up ``` Then start the Rank server from the repository root: ```sh npm start -- serve examples/s3-logs --dev ``` If you changed the LocalStack port, pass the matching endpoint to the Rank server: ```sh S3_ENDPOINT=http://127.0.0.1:4567 npm start -- serve examples/s3-logs --dev ``` Try it: ```sh curl http://127.0.0.1:3000/health curl http://127.0.0.1:3000/logs curl 'http://127.0.0.1:3000/logs?day=2026-05-11' ``` --- ## Serve — Docker Package a Rank HTTP app into a container image using `rank serve`. ## Source ```rank title="examples/serve-docker/main.rank" use std::HTTP HealthRoute = HTTP::Route { method: `GET`, path: `/health` } Routes = HealthRoute pub config = { defaultResponseFormat: `json` } pub main = || -> HTTP::Response { return { status: 200, body: { ok: true, service: `serve-docker-example` } } } ``` ```toml title="examples/serve-docker/rank.toml" manifestVersion = 1 [package] name = "serve-docker-example" version = "0.1.0" source = "." ``` ```dockerfile title="examples/serve-docker/Dockerfile" FROM node:22-slim WORKDIR /app RUN npm install -g @rank-lang/cli COPY rank.toml main.rank ./ RUN rank sync . EXPOSE 3000 CMD ["rank", "serve", ".", "--host", "0.0.0.0", "--port", "3000"] ``` ## Build and run ```sh docker build -t rank-serve-example . docker run --rm -p 3000:3000 rank-serve-example ``` ```sh curl http://127.0.0.1:3000/health ``` ```json { "ok": true, "service": "serve-docker-example" } ``` For local parity without Docker: ```sh rank serve . --host 127.0.0.1 --port 3000 ``` ## Key concepts - **`rank sync .`** — runs during the Docker build to cache all registry dependencies. The container starts without making outbound package requests. - **`--host 0.0.0.0`** — binds to all interfaces so traffic forwarded by Docker reaches the server. In local development use `127.0.0.1` (the default). - **`pub config`** — sets `defaultResponseFormat: \`json\`` so all route responses are serialized as JSON without having to specify `format` on each one. - **`/.well-known/rank/ready`** — built-in readiness endpoint managed by the server runtime, not part of the application route table. Use it for Docker health checks or load-balancer probes. --- ## Serve — Lambda Web Adapter Deploy a Rank HTTP app to AWS Lambda using the [Lambda Web Adapter](https://github.com/awslabs/aws-lambda-web-adapter) and a `provided.al2023` custom runtime — no container image required. The adapter is attached as a Lambda Layer. It translates Lambda invocations into HTTP requests forwarded to `rank serve`, so the Rank app needs no Lambda-specific handler code. ## Source ```rank title="examples/serve-lambda/main.rank" use std::HTTP use std::Runtime HealthRoute = HTTP::Route { method: `GET`, path: `/health` } Routes = HealthRoute pub config = { defaultResponseFormat: `json` } pub main = |req: Runtime::ExecutionContext| -> HTTP::Response { return { status: 200, body: { ok: true, service: `serve-lambda-web-adapter-example` } } } ``` ```sh title="examples/serve-lambda/bootstrap" #!/bin/sh export PATH="$LAMBDA_TASK_ROOT/bin:$LAMBDA_TASK_ROOT/node_modules/.bin:$PATH" exec rank serve "$LAMBDA_TASK_ROOT" --host 0.0.0.0 --port "${PORT:-8080}" ``` ```makefile title="examples/serve-lambda/Makefile" .PHONY: build-RankApp build-RankApp: dnf install -y nodejs npm 2>/dev/null || true npm install --prefix "$(ARTIFACTS_DIR)" @rank-lang/cli "$(ARTIFACTS_DIR)/node_modules/.bin/rank" sync . cp main.rank rank.toml "$(ARTIFACTS_DIR)/" cp bootstrap "$(ARTIFACTS_DIR)/" chmod +x "$(ARTIFACTS_DIR)/bootstrap" mkdir -p "$(ARTIFACTS_DIR)/bin" cp "$$(which node)" "$(ARTIFACTS_DIR)/bin/" ``` ```yaml title="examples/serve-lambda/template.yaml" AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Globals: Function: Timeout: 30 MemorySize: 512 Resources: RankApp: Type: AWS::Serverless::Function Properties: Runtime: provided.al2023 Architectures: - x86_64 Handler: bootstrap CodeUri: ./ Layers: - !Sub arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:24 Environment: Variables: PORT: '8080' READINESS_CHECK_PATH: /.well-known/rank/ready Events: Api: Type: HttpApi Properties: Path: /{proxy+} Method: ANY RootApi: Type: HttpApi Properties: Path: / Method: ANY Metadata: BuildMethod: makefile Outputs: ApiUrl: Description: API Gateway endpoint Value: !Sub https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com ``` ## Build and deploy Requires the [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html). Docker is used only for the SAM container build step, not the Lambda runtime. There is no separate Rank compile step for this example. The Lambda source bundle is the example directory itself: - `main.rank` is the Rank app source. - `rank.toml` makes the directory runnable as `rank serve .`. - `bootstrap` is the custom runtime entrypoint Lambda executes. - `template.yaml` tells SAM to build the function with the local `Makefile` via `BuildMethod: makefile`. Run the build from `examples/serve-lambda`: ```sh cd examples/serve-lambda # Build inside an Amazon Linux 2023 container so node matches the runtime sam build --use-container # First deploy (creates samconfig.toml) sam deploy --guided ``` During `sam build`, SAM invokes the `build-RankApp` target from `Makefile` inside the AL2023 build container. The target name is `build-`, so it matches the function resource name `RankApp` in `template.yaml`. SAM also sets `ARTIFACTS_DIR` to the staging directory that will become the Lambda bundle. That target: 1. Installs Node.js via `dnf` 2. Installs `@rank-lang/cli` into the artifact directory 3. Runs `rank sync .` to pre-cache registry dependencies 4. Copies `main.rank`, `rank.toml`, and `bootstrap` into the Lambda artifact directory 5. Copies the node binary so it is available at `$LAMBDA_TASK_ROOT/bin/node` at runtime After the build, the staged Lambda bundle is under `.aws-sam/build/RankApp/`. That directory is what SAM deploys to Lambda. ## Test it ```sh curl https://.execute-api..amazonaws.com/health ``` ```json { "ok": true, "service": "serve-lambda-web-adapter-example" } ``` Local testing without Lambda: ```sh rank serve . --host 127.0.0.1 --port 8080 ``` Or with the SAM local emulator: ```sh sam local start-api --port 8080 ``` ## Key concepts - **`Runtime: provided.al2023`** — Lambda calls the `bootstrap` executable directly. No managed runtime is involved; rank serve is the only process. - **Lambda Web Adapter layer** — the `LambdaAdapterLayerX86` layer adds an extension that translates Lambda invocations into HTTP requests and forwards them to the server on `PORT`. - **`PORT=8080`** — the adapter forwards traffic to rank serve on this port. - **`READINESS_CHECK_PATH=/.well-known/rank/ready`** — the adapter polls this built-in endpoint on startup and withholds traffic until rank serve is ready to accept connections. - **`rank sync .` at build time** — caches registry dependencies so cold starts don't need outbound package requests. - **No Lambda handler** — the Rank app is unaware it runs on Lambda. The same `rank serve` command works locally and behind the adapter. --- ## Dotenv Config Read a project-local `.env` file with `File::Read`, decode scalar values at the boundary with `Decode`, and reshape the result into a typed runtime config. ## Source ```dotenv title="examples/dotenv-config/.env" APP_ENV=prod PORT=9000 DEBUG=true APP_NAME="Rank Docs" ``` ```rank title="examples/dotenv-config/main.rank" /// Reads a project-local dotenv file through File::Read with Decode /// at the flat text boundary and reshapes it into a typed runtime config. use std::Decode use std::File use std::Path AppEnv = `dev` | `prod` Port = 8080 | 9000 DotenvConfig = Object { APP_ENV: AppEnv, PORT?: Decode, DEBUG?: Decode, APP_NAME: string, ...: string, } AppConfig = Object { name: string, env: AppEnv, port: number, debug: bool, source: Object { path: string, format: `dotenv`, }, } response = File::Read { path: Path::join([`.env`]), format: `dotenv`, } raw = response.body config: AppConfig = { name: raw.APP_NAME, env: raw.APP_ENV, port: raw.PORT ?? 8080, debug: raw.DEBUG ?? false, source: { path: response.ctx.path, format: `dotenv`, }, } pub main = || config ``` ## Output ```json { "name": "Rank Docs", "env": "prod", "port": 9000, "debug": true, "source": { "path": ".env", "format": "dotenv" } } ``` ## Key concepts - **`format: dotenv`** — `File::Read` can decode `.env`-style flat text files as an explicit file dependency rather than ambient host environment. - **Boundary decoding with `Decode`** — `PORT?: Decode` and `DEBUG?: Decode` turn dotenv text into typed scalar values as the file is read. - **Flat text model** — each dotenv entry stays one key/value text boundary; nested objects and lists are not decoded from a single entry. - **`response.ctx`** — file provenance stays explicit, so the resulting config can still carry the source path and format. - **Defaults after decode** — optional decoded fields combine cleanly with `??` once the boundary parsing has succeeded. ## Run it ```sh rank examples/dotenv-config ```