---
title: Generate artifact metadata
editUrl: https://github.com/Cratis/Arc.TypeScript/edit/main/Documentation/proxy-generation/generated-artifact-metadata.md
description: Let Arc bind command and query parameters from TypeScript declarations without repeating class tokens in standard decorators.
---


TypeScript erases parameter types when it compiles a standard decorator. Generate server metadata from your artifact sources to keep the method declaration as the source of truth. The Tasks sample builds both client proxies and server metadata from the same project:

```bash
yarn tsc -b Source/Core Source/Tools/ProxyGenerator
yarn workspace @cratis/arc.core.sample.tasks generate-proxies
yarn build
```

The script passes `--project`, `--artifacts`, `--output`, and `--metadata Samples/Tasks/Features/generatedMetadata.ts` to `arc-proxygenerator`. The generated module is TypeScript, checked into the sample, and compiled along with the artifacts. `yarn build` checks its content against the current source and fails with a **regenerate artifact metadata** message when it is missing, changed, or stale. `yarn ci` checks the committed module before regenerating, so stale metadata fails CI. Run the generator before a `tsx` development server or Vitest run that uses the artifacts; do not rely on the test runner to discover erased types.

Import the generated module and install it **before** discovery or `add()`, as in the [Tasks bootstrap](/arc/backend/typescript/getting-started/#see-what-started-the-server). The `builder.useGeneratedMetadata(metadata)` call must precede `builder.discover(...)`.

Now the [sample command](https://github.com/Cratis/Arc.TypeScript/blob/main/Samples/Tasks/Features/Tasks/Registration/Registration.ts) uses `handle(tasks: Tasks)` without `@inject(Tasks)`, and a read-model method can use `@query() static taskById(id: TaskId, tasks: Tasks)`. Arc analyzes argument names and positions, concrete service classes (including nullable services, which receive `null` when unregistered), nullable/optional fields, validator targets, and the declared observable return. A union of two concrete service classes needs an explicit token. Keep `@field(Type)` on every wire property: that is the Cratis runtime schema convention, not redundant generated metadata. `provide()` still supplies one unmarked value as the first `handle()` argument; typed `provided(Type)` remains available for explicitly bound additional values.

For a development loop, run `arc-proxygenerator` with the same options plus `--watch`. It debounces changes under the artifacts root and watches referenced local source files outside that root. Run it in a separate terminal before your `tsx` or Vitest command; do not run a watched generator as a one-shot CI step. Use `--check-metadata` with the same arguments for a read-only build gate. The programmatic `generateFromSource({ project, artifacts, output, metadata })` writes the same module, while `renderGeneratedMetadata(project, artifacts, metadataFile)` returns its deterministic text without writing it. Paths in the programmatic generator are absolute, and the output folder must exist.

Explicit decorators remain useful when you cannot run a build step: `@inject(Tasks)` and `@query(argument('id', TaskId), service(Tasks))` override inferred bindings. They also let you inject an interface by giving Arc a concrete class or `serviceToken` to resolve. The analyzer cannot invent a runtime value for an interface or erased type; it reports the file and line and asks for an explicit token. `@validator(Target)` similarly overrides inferred validator targets. Legacy `experimentalDecorators` with `emitDecoratorMetadata` still handles decorated class-valued parameters without generation; standard decorators need either generated metadata or explicit bindings. Bare `@query()` in standard mode does not give a TS1241 parameter-count error without generation; `build()` rejects unbound parameters instead.

For commands, generated `handleResult` describes the **client response** after server-handled values are removed. When `handle()` returns events, operations, or a tuple with a handled value, `handleValueResult` separately retains the declared raw return cardinality for runtime validation; a returned event is not a client response. Regenerate both proxies and metadata together when changing command return types.

The generated module records a version and each class's runtime-checkable shape (fields, method names, and arities). `useGeneratedMetadata` rejects an incompatible version or shape at build time. **Reordering parameters without changing their count can silently bind the wrong dependency. Always run `--check-metadata` after editing an artifact, and retain class names when bundling (`keepNames` in esbuild).** Runtime reflection cannot see source-only changes such as parameter types or positions. Client-only generation without `--metadata` must opt into source-inferred bindings with `--use-generated-metadata` (or `generatedMetadata: true` programmatically); the presence of a generated file does not switch modes implicitly. Do not edit the generated module; regenerate it. The analyzer supports concrete exported class tokens, primitives, concepts, arrays of wire values, and declared query observable sources. This is a bounded source-analysis path, not full parity with the .NET Roslyn generators; see [capabilities](/arc/backend/typescript/reference/capabilities/).
