Skip to main content
@craft-ts
LearnLearn with EffectAI agentsGuideReference
@craft-ts/core@craft-ts/component@craft-ts/dev-tools@craft-ts/effect@craft-ts/style@craft-ts/style-testing@craft-ts/i18n@craft-ts/i18n-effect@craft-ts/mcp
ExamplesMigrationEffect compatibility & maturityAdopting CraftTS progressivelyPress kitRoadmapBacklog
Beta
  • Learn
  • Learn with Effect
  • AI agents
  • Guide
  • Reference
  • Packages
  • Resources
  • Guide overview
  • Create a project
i.Core concepts
  • The mental model
  • Which primitive should I use?
  • Anatomy of a primitive
  • Generators and yield*
  • Insertions
  • Typed insertion pipes
  • Exceptions as values
ii.Managing state
  • Local state
  • State machines
  • query
  • Mutations
  • queryParams
  • asyncProcess
  • Selecting
  • Reacting to mutations
  • Collections
  • Collection utilities
  • Persistence
  • GlobalPersisterHandler
  • Pagination placeholders
  • Schema validation
iii.Recommended approaches
  • Inject at the point of use
iv.Structuring the app
  • craftService
  • Service scopes
  • Shaping the public API
  • Abstract services
  • App start
  • Lazy services
  • Server functions
  • craftRegisterFor
  • Target wrapper
v.Routing & type-safe DI
  • Setup
  • CLI automation
  • ESLint rules
  • Route providers
  • Route guards
  • Route exception handling
  • Non-blocking navigation
  • Global error component
  • Route load errors
  • Scaling routes
vi.Components & templates
  • Components
  • Activating the style system
  • Typed styles
  • Defining a design system
  • Tokens and variables
  • Global foundation and fonts
  • Pseudo-elements and animations
  • Axes and the matrix
  • Context obligations
  • Testing visual states
  • Text contrast
  • Visual attestation
  • Template obligations
  • Typed CSS variables
  • Fine-grained reactivity
  • Progressive rendering
  • Directives and .pipe(...)
  • settledValue & pendingNode
  • Accessibility
  • Customization
  • Content projection
  • Styling a component
  • Template migrator
vii.Forms
  • Overview
  • Validators
  • Submitting
  • Nested forms
  • Exception handling
  • Complete examples
viii.Testing
  • Testing services
  • Testing components
  • Type-level tests
  • Browser boundaries
  • Page stability
  • Architecture rules
    • Overview
    • Declarative baseline
    • Unique identities
    • HTTP endpoint ownership
    • Computed purity
    • Dependency cycles
    • Mutation reactions
    • Route DI proofs
    • Route component files
    • Path boundaries
    • Exclusive links
    • Persisted identities
    • insertSelect keys
    • craftEffect and network
    • craftEffect and imperative sync
    • Interactive element names
    • Metric thresholds
    • Server-state loaders
    • Primitive loader requirements
  • Extensible architecture graph
  • Graph insights
  • Craft graph vs Nx
ix.Reactivity utilities
  • craftComputed
  • craftEffect
  • craftMethod
  • source$
  • on$
  • fromEventToSource$
  • sourceFromEvent
  • afterRecomputation
x.Internationalisation
  • Type-safe i18n
  • The catalogue
  • Tokens
  • The runtime
  • With Effect
xi.Deployment (experimental)
  • Overview
  • Manifest reference
  • Diagnostics
  • Providers
  • Alchemy provider
xii.Going further
  • SSR and hydration
  • Effect integration
  • Program operators
  • Pattern matching
  • Observability
  • AI agents
  • Temporal runtime
  1. Deployment (experimental)
  2. Manifest reference

Manifest reference#

Experimental

Manifest fields can still be added, renamed or removed between minor versions. The serialised form carries protocolVersion: '1', and a manifest produced by another protocol is refused rather than reinterpreted — so a change here breaks loudly, at the check, and comes with migration notes.

The manifest is what an application writes in craft.deploy.ts. It describes only the facts a build and a deployment need, and it stays provider-neutral: the provider is chosen when deploying, never to produce the artefact.

Accepted file names, in priority order: craft.deploy.ts, craft.deploy.mts, craft.deploy.mjs, craft.deploy.js, craft.deploy.json.

Common fields#

FieldRequiredDefaultMeaning
nameyes—Name of the deployment.
environmentnoproductionTarget environment, e.g. staging.
runtimeyes—static, node, worker or lambda.
platformyes—Technical platform executing that runtime.
clientdepends—Browser build command and output directory.
functionsno—Server-functions exposed by the deployment.
envno[]Environment variables expected, without their values.
artifactnoderivedWhat a provider ships, and the source map policy.

client is required for the static runtime and optional for the others.

Runtime sections#

The runtime discriminates which section is mandatory, at the type level and at the validation level. A section belonging to another runtime is refused: nothing would ever execute it.

static#

ts
static: {
  mode: 'spa' | 'ssg',
  fallback?: string,          // spa, default `index.html`
  routes?: readonly string[], // ssg, the routes to pre-render
  serverRoutes?: readonly string[],
}

In ssg mode the route list is mandatory and every route must reduce to a single document: /, /about, /blog/2026 are pre-renderable, /users/:id and /blog/* are not. Routes that need a server runtime go in serverRoutes, which documents the boundary instead of hiding it.

A route is considered rendered when the artefact holds either <route>.html or <route>/index.html.

server — the node runtime#

ts
server: {
  entry: string,        // SSR entry produced by the build
  source?: string,      // module producing `entry`
  build?: string,
  start?: string,
  healthPath: string,   // absolute path, e.g. `/health`
  readyPath: string,    // absolute path, e.g. `/ready`
}

healthPath and readyPath are mandatory because an SSR deployment without a readiness signal cannot be rolled out safely.

source is what makes craft-ts check useful before a build: the checker reads the module graph of the source, not of an output that does not exist yet.

worker — the worker runtime#

ts
worker: {
  entry: string,        // module exporting `fetch(request, env, ctx)`
  source?: string,
  build?: string,
  bindings?: readonly { name: string; type: string; description?: string }[],
}

Bindings are declared, never valued. createCraftWorkerFetch makes the HTTP application and the server-functions portable; it does not make an SSR entry that reaches for node:http or node:fs portable, which is exactly what the Node built-in check catches.

lambda — the lambda runtime#

ts
lambda: {
  entry: string,        // Function URL handler
  source?: string,
  build?: string,
  permissions?: readonly string[],
}

functions#

ts
functions: {
  entry: string,               // module building the server-function registry
  basePath?: string,           // default `/api`
  ids?: readonly string[],     // identifiers exposed to clients
}

The identifier is the routing key of the protocol, so a duplicate is refused. An identifier that never appears in the module graph of entry is reported as a warning.

env#

ts
env: [
  {
    name: 'PORT',
    required: false,
    description: 'TCP port the server listens on.',
  },
];

Names are upper snake case. A value or a default is refused: the manifest is committed and read by every provider, so it carries the contract, not the secret. Variables read by the runtime entry but absent from this list are reported as warnings.

artifact#

ts
artifact: {
  publicDir?: string,          // default: client.outDir
  serverEntry?: string,        // default: the runtime entry
  start?: string,              // default: server.start
  configFiles?: readonly string[],
  sourceMaps?: 'forbidden' | 'external' | 'allowed', // default: forbidden
}

The default source map policy is forbidden: shipping the maps of a production bundle publishes the sources, so an application has to opt out explicitly.

The resolved manifest#

craft-ts manifest applies every default and stamps the protocol version:

bash
npx craft-ts manifest --config apps/demo-ssr/craft.deploy.ts \
  --out dist/apps/demo-ssr/craft-deployment-manifest.json

The output has sorted keys, so two builds of the same input are byte-identical and a diff is reviewable. protocolVersion is 1; a manifest produced by another protocol is refused rather than reinterpreted.

A complete example#

The SSR demonstrator of this repository, apps/demo-ssr/craft.deploy.ts, checked on every production run:

ts
import { defineCraftDeployment } from '@craft-ts/deploy';

/**
 * Deployment of the SSR demo.
 *
 * Every path is relative to the workspace root, which is the directory
 * `craft-ts check` runs from. The manifest stays provider-neutral: it says
 * what the artefact is, never who publishes it.
 */
export default defineCraftDeployment({
  name: 'demo-ssr',
  environment: 'production',
  runtime: 'node',
  platform: 'docker',
  client: {
    build: 'nx run demo-ssr:build:production',
    outDir: 'dist/apps/demo-ssr',
  },
  server: {
    build: 'nx run demo-ssr:build:production',
    entry: 'dist/apps/demo-ssr/server/server.js',
    // The build output only exists after a build; declaring the source lets
    // `craft-ts check` read the real module graph before that.
    source: 'apps/demo-ssr/src/production-server.ts',
    start: 'node dist/apps/demo-ssr/server/server.js',
    healthPath: '/health',
    readyPath: '/ready',
  },
  functions: {
    entry: 'apps/demo-with-server-function/src/server/server.ts',
    basePath: '/api',
    ids: [
      'demo.products.list',
      'demo.users.list',
      'demo.users.authenticated-list',
      'demo.users.portable-list',
      'demo.users.effect-middleware-list',
    ],
  },
  env: [
    {
      name: 'HOST',
      required: false,
      description: 'Interface the server binds to.',
    },
    {
      name: 'PORT',
      required: false,
      description: 'TCP port the server listens on.',
    },
    {
      name: 'GRACEFUL_SHUTDOWN_TIMEOUT_MS',
      required: false,
      description: 'Delay granted to in-flight requests on SIGTERM.',
    },
    {
      name: 'RATE_LIMIT_MAX',
      required: false,
      description: 'Requests allowed per window on API routes.',
    },
    {
      name: 'RATE_LIMIT_WINDOW_MS',
      required: false,
      description: 'Length of the rate limiting window.',
    },
    {
      name: 'FORCE_HTTPS',
      required: false,
      description: 'Treat requests as HTTPS behind a trusted proxy.',
    },
    {
      name: 'CORS_ORIGINS',
      required: false,
      description: 'Comma-separated list of allowed origins.',
    },
    {
      name: 'TRUSTED_HOSTS',
      required: false,
      description: 'Comma-separated list of accepted Host headers.',
    },
    {
      name: 'PUBLIC_ORIGIN',
      required: false,
      description: 'Origin used to build canonical and Open Graph URLs.',
    },
  ],
  artifact: {
    publicDir: 'dist/apps/demo-ssr',
    configFiles: ['apps/demo-ssr/Dockerfile', 'docker-compose.production.yml'],
    sourceMaps: 'forbidden',
  },
});
←OverviewDiagnostics→

On this page

  • Common fields
  • Runtime sections
  • static
  • server — the node runtime
  • worker — the worker runtime
  • lambda — the lambda runtime
  • functions
  • env
  • artifact
  • The resolved manifest
  • A complete example

@craft-ts · MIT licensed

GitHubnpm

Search