Skip to Content
ErrorsNEXUS_SCOPE_REQUIRED

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_SCOPE_REQUIRED

NEXUS_SCOPE_REQUIRED means code asked the root container for a scoped provider or for REQUEST. The root has no scope, so get() throws it directly. So does resolve(), which throws for a lazy() entry when resolve() runs, if the target is a scoped provider, REQUEST or an alias to either. A transient provider that depends on a scoped one raises it too, and path then lists the transient first, as in path=Briefing,FlightLog. A lazy() thunk to such a transient throws when called, with entry set to null. A singleton that depends on a scoped provider fails earlier: Nexus.create reports NEXUS_LIFETIME_VIOLATION inside NEXUS_BLUEPRINT_INVALID.

Reproduce NEXUS_SCOPE_REQUIRED

The region binds FlightLog as scoped and asks the root for it. Without a plugin, core prints a one-line message that starts with the code and ends with the address of this page. With errors() from @nexusdi/errors registered (npm install @nexusdi/errors@next), the same error carries the full text and its fix.

import { Nexus, REQUEST, Token, defineModule } from '@nexusdi/core'; import { isNexusError } from '@nexusdi/core'; import { provide } from '@nexusdi/core'; import { errors } from '@nexusdi/errors'; interface Mission { readonly id: string; readonly target: string; } interface IFlightLog { readonly mission: Mission; } const MISSION = new Token<Mission>('Mission'); const FLIGHT_LOG = new Token<IFlightLog>('FlightLog'); class ShuttleFlightLog implements IFlightLog { static deps = [MISSION] as const; constructor(readonly mission: Mission) {} } const Tactical = defineModule({ name: 'Tactical', providers: [ provide(MISSION, { useFactory: (request) => request.mission, deps: [REQUEST], lifetime: 'scoped', }), provide(FLIGHT_LOG, { useClass: ShuttleFlightLog, lifetime: 'scoped' }), ], exports: [FLIGHT_LOG], }); const getLog = (ship: Nexus) => { try { return ship.get(FLIGHT_LOG); } catch (error) { return error; } }; await using ship = await Nexus.create(Tactical); const thin = getLog(ship); const line = isNexusError(thin) ? thin.message : null; // -> '[NEXUS_SCOPE_REQUIRED] token=FlightLog path=FlightLog. https://nexus.js.org/errors/NEXUS_SCOPE_REQUIRED' console.log(line); await using fullShip = await Nexus.create(Tactical, { plugins: [errors()] }); const full = getLog(fullShip); const text = isNexusError(full) ? full.message.split('\n') : null; // -> ['[NEXUS_SCOPE_REQUIRED] FlightLog is scoped, and the root container has no scope.', ' Fix: resolve it from a scope: const scope = await ship.createScope(); scope.get(FlightLog).'] console.log(text);

Try it: save the region as error.ts and run npx tsx error.ts. It prints the one-line message, then the full text as an array of two strings. The second string shows how to create a scope. The regions read request.mission, so save the request.d.ts file from Scopes and the REQUEST Token beside them and tsc agrees. Then save the fix region below as fixed.ts and run npx tsx fixed.ts. It prints Kepler-442b.

Fix NEXUS_SCOPE_REQUIRED

NEXUS_SCOPE_REQUIRED is fixed by resolving scoped providers from a scope. Scopes and the REQUEST Token covers scopes.

import { Nexus, REQUEST, Token, defineModule, provide } from '@nexusdi/core'; interface Mission { readonly id: string; readonly target: string; } interface IFlightLog { readonly mission: Mission; } const MISSION = new Token<Mission>('Mission'); const FLIGHT_LOG = new Token<IFlightLog>('FlightLog'); class ShuttleFlightLog implements IFlightLog { static deps = [MISSION] as const; constructor(readonly mission: Mission) {} } const Tactical = defineModule({ name: 'Tactical', providers: [ provide(MISSION, { useFactory: (request) => request.mission, deps: [REQUEST], lifetime: 'scoped', }), provide(FLIGHT_LOG, { useClass: ShuttleFlightLog, lifetime: 'scoped' }), ], exports: [FLIGHT_LOG], }); await using ship = await Nexus.create(Tactical); await using shuttle = await ship.createScope({ request: { mission: { id: 'survey-7', target: 'Kepler-442b' } }, }); const target = shuttle.get(FLIGHT_LOG).mission.target; // -> 'Kepler-442b' console.log(target);

Try it: in fixed.ts, change shuttle.get(FLIGHT_LOG) to ship.get(FLIGHT_LOG) and run it again. tsc accepts the change, because the types accept get() on a root and on a scope alike. tsx throws an uncaught ScopeRequiredError that starts with [NEXUS_SCOPE_REQUIRED] token=FlightLog path=FlightLog.. Change the call back and the program prints Kepler-442b again.

Fields of ScopeRequiredError

ScopeRequiredError carries these fields:

  • token: the display name of the scoped provider, or REQUEST.
  • path: the providers from the token asked for to the scoped one it reaches.
  • entry: the deps entry of resolve() this error is about, deps.<key> for the key of an object, such as deps.s, or deps[0] for a tuple, and null for get() or a thunk call.