Skip to Content
ErrorsNEXUS_CONTRACT_VERSION

This page documents the 0.4 release candidates of @nexusdi/core. The newest on npm is 0.4.0-rc.1. The documentation for 0.3.2, the current release, is at nexus.js.org. Try the release candidate.

NEXUS_CONTRACT_VERSION

federation() reports NEXUS_CONTRACT_VERSION when a dependent was built against a version of a contract that its provider’s version does not satisfy. The check looks at every dependency whose provider and consumer both come from defineContract(), and it runs as part of compiling the module graph, before any constructor runs. Nexus.create and ship.load() reject with NEXUS_BLUEPRINT_INVALID holding it. Nexus.check throws that error. No call raises NEXUS_CONTRACT_VERSION bare.

The rule is the one of npm’s ^ range. The majors must match. At major 1 and above, the dependent needs a minor no newer than the provider’s, and on the same minor a patch no newer than the provider’s. At major 0 the minors must match as well, because a minor is a breaking change there. A prerelease tag is ignored by the comparison. Each mismatch is reported once per check, however many dependents share it.

Reproduce NEXUS_CONTRACT_VERSION

The shell provides meridian/ReactorCore from version 2.3.0 of its contracts package, and a remote’s QuantumComputer was built against 2.4.0. Without a text pack, the message is one line that starts with the code and ends with the address of this page.

import { Nexus, Token, defineModule, provide } from '@nexusdi/core'; import { isNexusError } from '@nexusdi/core'; import { defineContract, federation } from '@nexusdi/federation'; interface IReactorCore { readonly output: number; } interface IShipComputer { readonly reactor: IReactorCore; } // The shell's copy of the contracts package, and a remote's newer copy. const shellContract = defineContract({ key: 'meridian', version: '2.3.0' }); const remoteContract = defineContract({ key: 'meridian', version: '2.4.0' }); const REACTOR = shellContract.token<IReactorCore>('ReactorCore'); const COMPUTER = new Token<IShipComputer>('ShipComputer'); class QuantumComputer implements IShipComputer { static deps = [remoteContract.token<IReactorCore>('ReactorCore')] as const; constructor(readonly reactor: IReactorCore) {} } const Meridian = defineModule({ name: 'Meridian', imports: [ defineModule({ name: 'Engineering', providers: [provide(REACTOR, { useValue: { output: 1.21 } })], exports: [REACTOR], global: true, }), defineModule({ name: 'Computers', providers: [provide(COMPUTER, { useClass: QuantumComputer })], exports: [COMPUTER], }), ], }); const error = await Nexus.create(Meridian, { plugins: [federation()] }).catch( (caught: unknown) => caught, ); const inner = isNexusError(error, 'NEXUS_BLUEPRINT_INVALID') ? error.errors[0] : undefined; const message = inner?.message; // -> '[NEXUS_CONTRACT_VERSION] contract=meridian/ReactorCore required=2.4.0 provided=2.3.0. https://nexus.js.org/errors/NEXUS_CONTRACT_VERSION' console.log(message);

Try it: run npm install @nexusdi/core@next @nexusdi/federation@next, save the region as contract.ts and run npx tsx contract.ts. It prints the one-line message: [NEXUS_CONTRACT_VERSION] contract=meridian/ReactorCore required=2.4.0 provided=2.3.0. and the address of this page. Then change the shell’s version to '2.5.0' and run it again. A newer minor satisfies the dependent, the container builds, and the region prints undefined because there is no error to read. Change it to '3.0.0' and the message returns with provided=3.0.0, because a new major is a breaking change. Change it back to '2.3.0' and the first message prints again.

Full text of NEXUS_CONTRACT_VERSION with errors()

NEXUS_CONTRACT_VERSION carries its full text and fix line when federationText from @nexusdi/federation/text is passed to errors() from @nexusdi/errors, as errors({ text: [federationText] }) in the plugins of Nexus.create. devtools({ text: [federationText] }) words the error the same way. The main entry of @nexusdi/federation never imports the pack, so an app that leaves it out includes none of its bytes.

import { Nexus, Token, defineModule, provide } from '@nexusdi/core'; import { isNexusError } from '@nexusdi/core'; import { errors } from '@nexusdi/errors'; import { defineContract, federation } from '@nexusdi/federation'; import { federationText } from '@nexusdi/federation/text'; interface IReactorCore { readonly output: number; } interface IShipComputer { readonly reactor: IReactorCore; } const shellContract = defineContract({ key: 'meridian', version: '2.3.0' }); const remoteContract = defineContract({ key: 'meridian', version: '2.4.0' }); const REACTOR = shellContract.token<IReactorCore>('ReactorCore'); const COMPUTER = new Token<IShipComputer>('ShipComputer'); class QuantumComputer implements IShipComputer { static deps = [remoteContract.token<IReactorCore>('ReactorCore')] as const; constructor(readonly reactor: IReactorCore) {} } const Meridian = defineModule({ name: 'Meridian', imports: [ defineModule({ name: 'Engineering', providers: [provide(REACTOR, { useValue: { output: 1.21 } })], exports: [REACTOR], global: true, }), defineModule({ name: 'Computers', providers: [provide(COMPUTER, { useClass: QuantumComputer })], exports: [COMPUTER], }), ], }); const error = await Nexus.create(Meridian, { plugins: [federation(), errors({ text: [federationText] })], }).catch((caught: unknown) => caught); const inner = isNexusError(error, 'NEXUS_BLUEPRINT_INVALID') ? error.errors[0] : undefined; const lines = inner?.message.split('\n'); // -> ['[NEXUS_CONTRACT_VERSION] meridian/ReactorCore is needed at 2.4.0, and the provider has 2.3.0.', ' Fix: build the provider against 2.4.0 or a newer 2.x, or build the dependent against 2.3.0.'] console.log(lines?.join('\n'));

Try it: run npm install @nexusdi/errors@next, save the region as full.ts and run npx tsx full.ts. It prints the two-line message with its Fix: line: [NEXUS_CONTRACT_VERSION] meridian/ReactorCore is needed at 2.4.0, and the provider has 2.3.0. and Fix: build the provider against 2.4.0 or a newer 2.x, or build the dependent against 2.3.0.

Fix NEXUS_CONTRACT_VERSION

NEXUS_CONTRACT_VERSION goes away when the provider’s contract version satisfies every dependent. Build the provider against the version the error names in required, or, at major 1 and above, a newer minor of the same major. Rebuilding the dependent against the version in provided clears the error too, since at major 1 and above an older dependent works with a newer provider. At major 0 the minors must match, and the provider’s patch must be no older than the dependent’s.

import { Nexus, Token, defineModule, provide } from '@nexusdi/core'; import { defineContract, federation } from '@nexusdi/federation'; interface IReactorCore { readonly output: number; } interface IShipComputer { readonly reactor: IReactorCore; } // The shell now provides the contract at 2.4.0, the version the remote needs. const shellContract = defineContract({ key: 'meridian', version: '2.4.0' }); const remoteContract = defineContract({ key: 'meridian', version: '2.4.0' }); const REACTOR = shellContract.token<IReactorCore>('ReactorCore'); const COMPUTER = new Token<IShipComputer>('ShipComputer'); class QuantumComputer implements IShipComputer { static deps = [remoteContract.token<IReactorCore>('ReactorCore')] as const; constructor(readonly reactor: IReactorCore) {} } const Meridian = defineModule({ name: 'Meridian', imports: [ defineModule({ name: 'Engineering', providers: [provide(REACTOR, { useValue: { output: 1.21 } })], exports: [REACTOR], global: true, }), defineModule({ name: 'Computers', providers: [provide(COMPUTER, { useClass: QuantumComputer })], exports: [COMPUTER], }), ], }); await using ship = await Nexus.create(Meridian, { plugins: [federation()] }); const output = ship.get(COMPUTER).reactor.output; // -> 1.21 console.log(output);

Try it: save the region as fixed.ts and run npx tsx fixed.ts. It prints 1.21. Change the shell’s version to '2.3.0' and run it again. npx tsc accepts the file, and npx tsx fixed.ts throws an uncaught BlueprintError and prints a stack trace headed BlueprintError: [NEXUS_BLUEPRINT_INVALID] 1 error., whose message goes on with the line [NEXUS_CONTRACT_VERSION] contract=meridian/ReactorCore required=2.4.0 provided=2.3.0. Change it back to '2.4.0' and the program prints 1.21 again.

Fields of ContractVersionError

  • code: 'NEXUS_CONTRACT_VERSION'.
  • contract: the contract key and token name, such as 'meridian/ReactorCore'.
  • required: the version the dependent was built against, as the string defineContract received, such as '2.4.0'.
  • provided: the version the provider was built against, in the same form.