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_ASYNC_TRANSIENT
NEXUS_ASYNC_TRANSIENT means a factory with lifetime: 'transient' returned a promise when the container built it. A transient builds at each request and get() is synchronous, so it cannot wait. get(), resolve(), the get() of a scope and a lazy() thunk throw it directly. When a provider that builds at create depends on the transient, create rejects with NEXUS_PROVIDER_FAILED and this error is its cause.
Reproduce NEXUS_ASYNC_TRANSIENT
TypeScript rejects an async transient factory where you write it. It reports TS2769 on the provide( call, and the error for the factory overload names this code:
Fails the type check
provide(DRONE, {
useFactory: async () => ({ serial: 7 }),
lifetime: 'transient',
});
// TS2769: No overload matches this call. The error for the factory overload ends with:
// Type '"transient"' is not assignable to type '"singleton" | "scoped" | "NEXUS_ASYNC_TRANSIENT: get() is synchronous, so a transient factory cannot be async. Use lifetime: 'scoped', or make the token a function type and provide () => Promise<T>" | undefined'.The error reaches run time when the types cannot see the promise, as with a JavaScript helper whose typings are wrong. The region uses such a helper. 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, Token, defineModule, isNexusError } from '@nexusdi/core';
import { provide } from '@nexusdi/core';
import { errors } from '@nexusdi/errors';
interface ISurveyDrone {
readonly serial: number;
}
const DRONE = new Token<ISurveyDrone>('SurveyDrone');
// launchDrone stands in for a JavaScript helper whose typings say it returns
// a drone. At run time it returns a promise, which TypeScript cannot see.
const launchDrone = (async (serial: number) => ({ serial })) as unknown as (
serial: number,
) => ISurveyDrone;
const Tactical = defineModule({
name: 'Tactical',
providers: [
provide(DRONE, {
useFactory: () => launchDrone(7),
lifetime: 'transient',
}),
],
exports: [DRONE],
});
const getDrone = (ship: Nexus) => {
try {
return ship.get(DRONE);
} catch (error) {
return error;
}
};
await using ship = await Nexus.create(Tactical);
const thin = getDrone(ship);
const line = isNexusError(thin) ? thin.message : null; // -> '[NEXUS_ASYNC_TRANSIENT] token=SurveyDrone module=Tactical. https://nexus.js.org/errors/NEXUS_ASYNC_TRANSIENT'
console.log(line);
await using fullShip = await Nexus.create(Tactical, { plugins: [errors()] });
const full = getDrone(fullShip);
const text = isNexusError(full) ? full.message.split('\n') : null; // -> ['[NEXUS_ASYNC_TRANSIENT] SurveyDrone (module Tactical) is transient and its factory returned a promise. get() is synchronous and cannot wait for it.', " Fix: use lifetime: 'scoped', or make the token a function type and provide () => Promise<T>."]
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. Then save the fix region below as fixed.ts and run npx tsx fixed.ts. It prints 7.
Fix NEXUS_ASYNC_TRANSIENT
NEXUS_ASYNC_TRANSIENT is fixed by giving the factory a lifetime that the container builds ahead of get(): singleton, built at create, or scoped, built at createScope. Here the drone is scoped, and the program asks a scope for it. To start async work on every call, make the token a function type and provide () => Promise<T>.
import { Nexus, Token, defineModule, provide } from '@nexusdi/core';
interface ISurveyDrone {
readonly serial: number;
}
const DRONE = new Token<ISurveyDrone>('SurveyDrone');
async function launchDrone(serial: number): Promise<ISurveyDrone> {
return { serial };
}
const Tactical = defineModule({
name: 'Tactical',
providers: [
provide(DRONE, {
useFactory: () => launchDrone(7),
lifetime: 'scoped',
}),
],
exports: [DRONE],
});
await using ship = await Nexus.create(Tactical);
await using shuttle = await ship.createScope();
const serial = shuttle.get(DRONE).serial; // -> 7
console.log(serial);Try it: in fixed.ts, change lifetime: 'scoped' to lifetime: 'transient' and run it again. tsc reports TS2769 on the provide( call, with the message shown above. tsx skips the type check, and shuttle.get(DRONE) throws an uncaught AsyncTransientError that starts with [NEXUS_ASYNC_TRANSIENT] token=SurveyDrone module=Tactical.. Put 'scoped' back and the program prints 7 again.
Fields of AsyncTransientError
AsyncTransientError carries these fields:
token: the display name of the transient provider.module: its module.