Skip to content
Merged
Show file tree
Hide file tree
Changes from 114 commits
Commits
Show all changes
137 commits
Select commit Hold shift + click to select a range
6b174de
add config options
ysmolski Jan 22, 2026
25900b9
test how modules are support
ysmolski Jan 23, 2026
c899d24
test flooring of ListSize
ysmolski Jan 23, 2026
85897a0
update with race fix in the engine
ysmolski Jan 26, 2026
db1e637
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Jan 27, 2026
cbac743
bump the engine
ysmolski Jan 27, 2026
551b21d
use released engine
ysmolski Jan 27, 2026
98e96f3
fix the comment in a test
ysmolski Jan 27, 2026
e21a880
fix default config
ysmolski Jan 27, 2026
5783522
return cost struct instead of the plan value
ysmolski Jan 30, 2026
466a3ca
rename config variables
ysmolski Jan 30, 2026
f7eb41b
rename config vars
ysmolski Jan 30, 2026
56d8d35
rename for the engine with actual costs
ysmolski Feb 6, 2026
12c6675
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Feb 9, 2026
b25430c
add rough metrics collection
ysmolski Feb 9, 2026
1e7e63a
remove test
ysmolski Feb 9, 2026
2f2bff7
use latest unreleased engine
ysmolski Feb 9, 2026
4793b16
add more metrics tests
ysmolski Feb 9, 2026
ca852d5
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Feb 9, 2026
1d6fcb3
clarify tests
ysmolski Feb 9, 2026
0ed9f40
require EstimatedListSize and fix MaxEstimatedLimit
ysmolski Feb 16, 2026
c79d56f
add attributes check to metrics test
ysmolski Feb 16, 2026
b164ca8
fix small nits
ysmolski Feb 16, 2026
d6ae666
add checks to a test
ysmolski Feb 16, 2026
61138f2
throw error defensively
ysmolski Feb 16, 2026
de2eca3
calculate delta as estimated - actual
ysmolski Feb 16, 2026
7d922d5
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Feb 16, 2026
02d359c
update gomod
ysmolski Feb 16, 2026
e7e410a
fix config
ysmolski Feb 16, 2026
02982bf
bump engine
ysmolski Feb 16, 2026
5a89487
hide telemetry under the switch
ysmolski Feb 17, 2026
275152a
add dedicated benchmark for the costs analysis
ysmolski Feb 17, 2026
5179643
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Feb 17, 2026
7656c45
bump the engine
ysmolski Feb 17, 2026
af815be
fix configs
ysmolski Feb 17, 2026
751dad2
Merge branch 'main' into yury/eng-8636-router-expose-the-static-cost-…
ysmolski Feb 18, 2026
41a091e
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Feb 19, 2026
aa6ab5e
bump engine to 253
ysmolski Feb 19, 2026
f470888
fix test check and add stack for debugging
ysmolski Feb 20, 2026
e503e8f
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Feb 20, 2026
fe6fd28
Merge branch 'main' into yury/eng-8636-router-expose-the-static-cost-…
ysmolski Feb 20, 2026
3127219
implement interface from composition with configs
ysmolski Feb 23, 2026
ef8fc58
calculate costs only once & expose via headers
ysmolski Feb 23, 2026
19acb89
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Feb 23, 2026
8ad9f94
remove unused var
ysmolski Feb 23, 2026
99edd25
diversify tests a little
ysmolski Feb 24, 2026
3e5e0c5
add directives to the schemas
ysmolski Feb 24, 2026
8dd3980
remove debug statement
ysmolski Feb 24, 2026
2e4eb46
remove delta metrics as redundant
ysmolski Feb 24, 2026
5c23d7e
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Feb 24, 2026
76b1b0b
add special bucket boundaries for cost histograms
ysmolski Feb 25, 2026
df343a2
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Feb 25, 2026
fdad133
tighten the range
ysmolski Feb 25, 2026
e601a8e
port the changes in composition
ysmolski Feb 27, 2026
445f8e5
feat(composition): support @cost and @listSize (#2484)
Aenimus Feb 27, 2026
ca941bf
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Feb 27, 2026
cc2df00
use Cost Control everywhere
ysmolski Feb 27, 2026
5e14201
rename leftovers
ysmolski Feb 27, 2026
e72353a
fix the last old name
ysmolski Feb 27, 2026
d7f4ae1
fix subtle edge cases
ysmolski Feb 27, 2026
be923bb
remove dupl entry
ysmolski Feb 27, 2026
59140e6
preserve requireOneSlicingArgument default (true)
ysmolski Feb 27, 2026
5c9a33b
emit actual-cost metric independently from estimated-cost
ysmolski Feb 27, 2026
878532f
dont ignore err in a test
ysmolski Feb 27, 2026
8084fd6
fix: listSize validation in composition
ysmolski Mar 2, 2026
0bd10e5
do not allow empty listSize on non-list types
ysmolski Mar 2, 2026
ba156cc
fix: reject costs defined on fields of interfaces
ysmolski Mar 2, 2026
c8afa3a
reject empty sizeField on non-list types
ysmolski Mar 2, 2026
1a8061e
tighten tests data
ysmolski Mar 2, 2026
1f8b392
add tests extension type
ysmolski Mar 2, 2026
32cda8c
update index
ysmolski Mar 2, 2026
febebbe
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Mar 2, 2026
b2bf4eb
fix lint issues
ysmolski Mar 2, 2026
18c6b04
continue for wrong entries
ysmolski Mar 2, 2026
583bdb1
update index
ysmolski Mar 2, 2026
2e232d6
add type weight and slicingArguments tests
ysmolski Mar 2, 2026
b8dc37f
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Mar 4, 2026
47df6d6
fix computation on cache hits
ysmolski Mar 9, 2026
1e04d27
add missing `cost_stats` in JSON schema
ysmolski Mar 9, 2026
523d665
be more strict in config validation
ysmolski Mar 9, 2026
5bd57c7
add test to verify both DS are used in Costs calc
ysmolski Mar 9, 2026
62cf79c
make the costHeaderSetter closure to use pw.writer.Header()
ysmolski Mar 9, 2026
a572d9a
remove redundant line
ysmolski Mar 9, 2026
568d06c
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Mar 9, 2026
9ca28e6
make lint happy
ysmolski Mar 9, 2026
6a741bf
comp: test the same field in two different subgraphs
ysmolski Mar 10, 2026
3397ae4
update test configs
ysmolski Mar 10, 2026
1eb75e5
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Mar 10, 2026
a3cf3bc
fixed typing issues
ysmolski Mar 10, 2026
992726b
use switch instead ifs in the handleListSizeDirective
ysmolski Mar 10, 2026
7165fec
update globals
ysmolski Mar 10, 2026
f001bb8
make lint
ysmolski Mar 10, 2026
61c787e
handle no parents case
ysmolski Mar 10, 2026
9077f1a
braces in switch
ysmolski Mar 11, 2026
63ff3e5
update global
ysmolski Mar 11, 2026
7bb3d37
fix bug in tests and add nil check
ysmolski Mar 11, 2026
a4ab87d
add parallel benchmark
ysmolski Mar 11, 2026
528f68d
add tests for negative weights
ysmolski Mar 11, 2026
a875c7d
use records
ysmolski Mar 11, 2026
af61281
handle costs in controlplane
ysmolski Mar 11, 2026
748122c
add else to if
ysmolski Mar 11, 2026
1fc29b8
update global
ysmolski Mar 11, 2026
35c36ad
fix small nits
ysmolski Mar 12, 2026
a87a413
use maps in the Costs type
ysmolski Mar 12, 2026
ef7e973
make argumentWeights as a map
ysmolski Mar 12, 2026
79b37ff
update global
ysmolski Mar 12, 2026
97c8062
make two properties defined always
ysmolski Mar 12, 2026
1c1f262
strict testing messages
ysmolski Mar 12, 2026
66d55fa
update global
ysmolski Mar 12, 2026
f5f57a1
make requireOneSlicingArgument set to true by default
ysmolski Mar 12, 2026
52ba20b
use toStrictEqual in all tests
ysmolski Mar 12, 2026
26297a9
check the argument type for weights on the directives arguments
ysmolski Mar 12, 2026
d78f088
sort the props
ysmolski Mar 12, 2026
884d2fc
remove ternary operator
ysmolski Mar 12, 2026
b8f768e
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Mar 13, 2026
cde508d
fix small things
ysmolski Mar 13, 2026
3218c53
fix lint for shared
ysmolski Mar 13, 2026
33159a5
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Mar 13, 2026
6bd6cb5
bump engine to 264
ysmolski Mar 13, 2026
374f111
fix tests
ysmolski Mar 13, 2026
cc7421a
add docs for sizedFields and enable cost control page
ysmolski Mar 13, 2026
d5e0a30
add tests for sizedFields
ysmolski Mar 16, 2026
1d3dad2
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Mar 16, 2026
c3c6db2
move config validation to the json schema layer
ysmolski Mar 16, 2026
612e9d4
reduce cardinality of histograms
ysmolski Mar 16, 2026
5676a71
test websockets both for queries and subs
ysmolski Mar 16, 2026
4ce7ad0
remove for for old controlplane
ysmolski Mar 16, 2026
a80e85a
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Mar 16, 2026
71398c6
add costs back to controlplane
ysmolski Mar 16, 2026
22eeec2
fix test affected by sizedFields and move costs_test.go
ysmolski Mar 17, 2026
92377ef
forbid listSize.sizedFields on lists returning types
ysmolski Mar 17, 2026
23067e7
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Mar 17, 2026
7b85244
update global
ysmolski Mar 17, 2026
037d6c9
add a deep lists test
ysmolski Mar 17, 2026
a701d0b
pnpm lint
ysmolski Mar 17, 2026
68713ca
Merge branch 'main' of github.com:wundergraph/cosmo into yury/eng-863…
ysmolski Mar 17, 2026
864dfa3
bump engine to 265
ysmolski Mar 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cli/src/commands/router/commands/compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ function constructRouterSubgraph(result: FederationSuccess, s: SubgraphMetadata,
const subgraphConfig = result.subgraphConfigBySubgraphName.get(s.name);
const schema = subgraphConfig?.schema;
const configurationDataByTypeName = subgraphConfig?.configurationDataByTypeName;
const costs = subgraphConfig?.costs;

if (s.kind === SubgraphKind.Standard) {
const composedSubgraph: ComposedSubgraph = {
Expand All @@ -125,6 +126,7 @@ function constructRouterSubgraph(result: FederationSuccess, s: SubgraphMetadata,
websocketSubprotocol: s.websocketSubprotocol,
schema,
configurationDataByTypeName,
costs,
};
return composedSubgraph;
}
Expand All @@ -141,6 +143,7 @@ function constructRouterSubgraph(result: FederationSuccess, s: SubgraphMetadata,
version: s.version,
schema,
configurationDataByTypeName,
costs,
};
return composedSubgraphPlugin;
}
Expand All @@ -155,6 +158,7 @@ function constructRouterSubgraph(result: FederationSuccess, s: SubgraphMetadata,
mapping: s.mapping,
schema,
configurationDataByTypeName,
costs,
};
return composedSubgraphGRPC;
}
Expand Down Expand Up @@ -648,6 +652,7 @@ async function buildFeatureFlagsConfig(
const subgraphConfig = featureResult.subgraphConfigBySubgraphName.get(s.name);
const schema = subgraphConfig?.schema;
const configurationDataByTypeName = subgraphConfig?.configurationDataByTypeName;
const costs = subgraphConfig?.costs;

const composedSubgraph: ComposedSubgraph = {
kind: SubgraphKind.Standard,
Expand All @@ -660,6 +665,7 @@ async function buildFeatureFlagsConfig(
websocketSubprotocol: s.websocketSubprotocol,
schema,
configurationDataByTypeName,
costs,
};
return composedSubgraph;
}),
Expand Down
402 changes: 201 additions & 201 deletions composition-go/index.global.js

Large diffs are not rendered by default.

326 changes: 326 additions & 0 deletions composition/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
# Composition Architecture and Onboarding (TypeScript)

This is a beginner-friendly guide to the `composition` package.

Scope:

- In scope: `composition/*`
- Out of scope: `composition-go/*`

## Who this is for

Use this doc if you are:

- new to this repository,
- trying to understand where composition errors come from,
- adding or shipping custom directives.

## 1. What this package produces

Given one or more subgraph SDLs, composition produces:

- a federated router schema,
- a federated client schema,
- router configuration metadata,
- warnings and errors.

Main API exports:

- `composition/src/index.ts`

Main functions:

- `federateSubgraphs(...)`
- `federateSubgraphsWithContracts(...)`
- `federateSubgraphsContract(...)`
- `normalizeSubgraph(...)`
- `batchNormalize(...)`

## 2. Mental model (simple)

Think of composition as a 4-stage pipeline:

1. Normalize each subgraph.
2. Batch-check all normalized subgraphs together.
3. Merge everything into one federated model.
4. Validate resolvability and emit final schemas.

```mermaid
flowchart LR
A["Subgraph SDLs"] --> B["NormalizationFactory<br/>(per subgraph)"]
B --> C["batchNormalize<br/>(cross-subgraph checks)"]
C --> D["FederationFactory<br/>(merge + build schemas)"]
D --> E["Graph.validate<br/>(resolvability)"]
E --> F["FederationResult<br/>AST + schema + config + warnings/errors"]
```

## 3. Where each stage lives

### Stage 1: Normalize one subgraph

Main files:

- `composition/src/v1/normalization/normalization-factory.ts`
- `composition/src/v1/normalization/walkers.ts`
- `composition/src/v1/normalization/utils.ts`

What happens here:

- Parse and validate SDL and directives.
- Build internal type/field model (`ParentDefinitionData`, `FieldData`, etc.).
- Validate federation directives (`@key`, `@requires`, `@provides`, `@override`, etc.).
- Build type-level router configuration (`ConfigurationData`).
- Record graph edges for later resolvability checks.

### Stage 2: Batch normalization

Main file:

- `composition/src/v1/normalization/normalization-factory.ts` (`batchNormalize`)

What happens here:

- Validate subgraph naming/uniqueness.
- Normalize each subgraph into shared structures.
- Merge cross-subgraph metadata (entities, auth, overrides, coordinates).
- Produce `internalSubgraphBySubgraphName` used by federation.

### Stage 3: Federation merge

Main file:

- `composition/src/v1/federation/federation-factory.ts`

What happens here:

- Merge all normalized types and fields.
- Reconcile incompatible shapes and directive usage.
- Build router/client schema definitions.
- Build final field configuration output.

### Stage 4: Resolvability validation

Main files:

- `composition/src/resolvability-graph/graph.ts`
- `composition/src/resolvability-graph/graph-nodes.ts`

What happens here:

- Validate that required field paths are actually reachable.
- Return errors if fields cannot be resolved across subgraphs.

## 4. Request flow from public API

Top-level function:

- `composition/src/federation/federation.ts`

Current compatibility:

- only version `"1"` is supported (`composition/src/router-compatibility-version/router-compatibility-version.ts`).

Effective flow:

1. `federateSubgraphs(...)`
2. `initializeFederationFactory(...)`
3. `batchNormalize(...)`
4. `FederationFactory.federateSubgraphData()`
5. `FederationFactory.buildFederationResult()`

## 5. Core data structures (quick glossary)

Result types:

- `composition/src/federation/types.ts`
- `composition/src/normalization/types.ts`
- `composition/src/subgraph/types.ts`

Most important internals:

- `InternalSubgraph`: normalized per-subgraph state fed into federation.
- `ParentDefinitionData`: canonical representation of a type in normalization/federation.
- `PersistedDirectiveDefinitionData`: executable directive definition info considered for federated schema persistence.
- `ConfigurationData`: per-type router configuration source.

## 6. Directives: how the system is designed

There are two broad categories:

1. Built-in directives with composition semantics.
2. Custom executable directives that can be validated and persisted.

### Built-in directives

Definition and registration:

- `composition/src/v1/constants/directive-definitions.ts`
- `composition/src/v1/constants/constants.ts`
- `composition/src/v1/constants/strings.ts`
- `composition/src/utils/string-constants.ts`

Normalization uses a directive registry (`directiveDefinitionDataByName`) to validate use sites and arguments.

### Custom directives

During normalization, unknown directive definitions are treated as custom:

- parsed and validated,
- added to normalized directive maps,
- included in normalized subgraph AST.

Key implementation points:

- `upsertDirectiveSchemaAndEntityDefinitions(...)`
- `addDirectiveDefinitionDataByNode(...)`

Both are in:

- `composition/src/v1/normalization/normalization-factory.ts`
- `composition/src/v1/normalization/walkers.ts`

### Directive persistence across subgraphs

During federation, executable directive definitions are only persisted when compatible across subgraphs.

Compatibility rules (important):

- The definition must exist consistently across participating subgraphs.
- Executable locations are intersected (must remain non-empty).
- Argument definitions must merge cleanly.
- Repeatability must remain compatible.

Core methods:

- `upsertPersistedDirectiveDefinitionData(...)`
- `addValidPersistedDirectiveDefinitionNodeByData(...)`

Files:

- `composition/src/v1/federation/federation-factory.ts`
- `composition/src/schema-building/utils.ts`

### Caveat: `@composeDirective`

`@composeDirective` is declared but currently unimplemented in behavior.

Reference:

- `composition/src/v1/constants/directive-definitions.ts`

Treat it as not available for feature design right now.

## 7. Shipping a custom directive (practical playbook)

If you want a custom executable directive to survive composition:

1. Define the directive in every relevant subgraph SDL.
2. Keep argument names/types/defaults compatible.
3. Keep executable locations compatible.
4. Use it where needed.
5. Compose and verify in the federated output.

Validation checklist:

- Definition exists in federated schema.
- Usages appear on expected nodes.
- No invalid repeatability errors.
- No cross-subgraph definition merge errors.

Best tests to copy:

- `composition/tests/v1/directives/directives.test.ts`
- `composition/tests/v1/normalization.test.ts`

## 8. Adding a new built-in directive (when persistence is not enough)

Use this path if you need new behavior (not just persistence), for example router config generation.

Implementation sequence:

1. Add directive name constants.
2. Add canonical directive definition node.
3. Add directive definition data (arg/location validation model).
4. Register in directive maps.
5. Add validation/extraction behavior in normalization.
6. Add federation persistence/merge behavior.
7. Add dependencies for helper scalars/inputs if needed.
8. Add tests (invalid, valid, merge, config impact).

Files usually touched:

- `composition/src/utils/string-constants.ts`
- `composition/src/v1/constants/directive-definitions.ts`
- `composition/src/v1/normalization/directive-definition-data.ts`
- `composition/src/v1/constants/constants.ts`
- `composition/src/v1/constants/strings.ts`
- `composition/src/v1/normalization/normalization-factory.ts`
- `composition/src/v1/federation/federation-factory.ts`
- `composition/tests/v1/directives/*.test.ts`

## 9. Contracts in one minute

`federateSubgraphsWithContracts(...)` flow:

1. Build the base federated graph.
2. Clone federation state per contract.
3. Apply tag include/exclude options per contract.
4. Return one result per contract.

If base federation fails, contracts are not attempted.

Main implementation:

- `composition/src/v1/federation/federation-factory.ts`

## 10. Debugging workflow for beginners

When composition fails:

1. Start at API entry:

- `composition/src/federation/federation.ts`

2. Reproduce with minimal SDL in tests:

- `composition/tests/v1/directives/*`

3. Inspect normalization output first:

- `directiveDefinitionByName`
- `parentDefinitionDataByTypeName`
- `configurationDataByTypeName`

4. Inspect federation state second:

- `parentDefinitionDataByTypeName`
- `potentialPersistedDirectiveDefinitionDataByDirectiveName`
- `referencedPersistedDirectiveNames`
- `errors` / `warnings`

5. If merge looks fine but final result fails, inspect resolvability:

- `Graph.validate()` path in `composition/src/resolvability-graph/graph.ts`

## 11. Recommended reading order (first week)

1. `composition/src/federation/federation.ts`
2. `composition/src/v1/federation/federation-factory.ts` (start from exported functions at the bottom, then move upward)
3. `composition/src/v1/normalization/normalization-factory.ts`
4. `composition/src/v1/normalization/walkers.ts`
5. `composition/src/resolvability-graph/graph.ts`
6. `composition/tests/v1/directives/*`

## 12. Quick FAQ

### Why do I get normalization errors before federation errors?

Because federation only runs after batch normalization succeeds. Normalization is the gate.

### Why did my custom directive disappear from federated output?

Usually because the definition is incompatible across subgraphs (locations, args, repeatability, or presence).

### Can I rely on `@composeDirective` behavior?

Not yet. It is declared, but composition behavior for it is not implemented.
6 changes: 6 additions & 0 deletions composition/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
The WunderGraph composition library facilitates the federation of multiple subgraph schemas into a
single federated GraphQL schema.

## Architecture and onboarding

For an implementation-level walkthrough of the composition pipeline and extension points (including shipping custom directives), see:

- [ARCHITECTURE.md](./ARCHITECTURE.md)

### Prerequisites

- [Node.js 16 LTS or higher](https://nodejs.dev/en/about/releases/)
Expand Down
Loading
Loading