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_INTERCEPTORS_SHARED
NEXUS_INTERCEPTORS_SHARED means a second Nexus.create received an interceptors() plugin object that another container still holds. The plugin object keeps the interceptor instances of one container, so each container needs its own interceptors() call. A plugin object whose container is disposed, or whose create failed, can be used again.
Where the error arrives depends on when the second create meets the first container. A second create that compiles while the first container is running rejects with NEXUS_BLUEPRINT_INVALID, and its errors list holds this error with core’s one-line message. A second create that overlaps the first, so that both compile before either builds, rejects with NEXUS_PROVIDER_FAILED. Its cause is a NEXUS_PLUGIN_FAILED for the construct hook, and that error’s cause is this error with its full text.
Nexus.check reports nothing for this code, even with the plugin object of a running container.
Reproduce NEXUS_INTERCEPTORS_SHARED
The second Nexus.create reports NEXUS_INTERCEPTORS_SHARED when it receives the plugin object that ship still holds. 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 { interceptor, interceptors } from '@nexusdi/interceptors';
import type { CallContext, Interceptor, Next } from '@nexusdi/interceptors';
interface IReactorCore {
output(): number;
}
const REACTOR = new Token<IReactorCore>('ReactorCore');
const AUDIT = new Token<Interceptor>('Audit');
class AuditInterceptor implements Interceptor {
intercept(_call: CallContext, next: Next) {
return next();
}
}
class FusionReactor implements IReactorCore {
output() {
return 1.21;
}
}
const Engineering = defineModule({
name: 'Engineering',
providers: [provide(REACTOR, { useClass: FusionReactor })],
exports: [REACTOR],
});
// One plugin object, passed to two containers that are open at once.
const shared = interceptors({
register: [interceptor(AUDIT, { useClass: AuditInterceptor })],
global: [AUDIT],
});
await using _ship = await Nexus.create(Engineering, { plugins: [shared] });
const error = await Nexus.create(Engineering, { plugins: [shared] }).catch(
(caught: unknown) => caught,
);
const inner = isNexusError(error, 'NEXUS_BLUEPRINT_INVALID')
? error.errors[0]
: undefined;
const message = inner?.message; // -> '[NEXUS_INTERCEPTORS_SHARED] https://nexus.js.org/errors/NEXUS_INTERCEPTORS_SHARED'
console.log(message);Try it: run npm install @nexusdi/core@next @nexusdi/interceptors@next, save the region as shared.ts and run npx tsx shared.ts. It prints the one-line message: [NEXUS_INTERCEPTORS_SHARED] and the address of this page. Replace shared in the second Nexus.create with a new interceptors({ ... }) call that has the same register and global options and run it again. Both containers build, and the region prints undefined because there is no error to read. Put shared back and the message returns.
The overlapping case needs two create calls in flight at once, as Promise.allSettled starts them here. The first fulfils and the second rejects.
import { Nexus, Token, defineModule, provide } from '@nexusdi/core';
import { isNexusError } from '@nexusdi/core';
import { interceptor, interceptors } from '@nexusdi/interceptors';
import type { CallContext, Interceptor, Next } from '@nexusdi/interceptors';
interface IReactorCore {
output(): number;
}
const REACTOR = new Token<IReactorCore>('ReactorCore');
const AUDIT = new Token<Interceptor>('Audit');
class AuditInterceptor implements Interceptor {
intercept(_call: CallContext, next: Next) {
return next();
}
}
class FusionReactor implements IReactorCore {
output() {
return 1.21;
}
}
const Engineering = defineModule({
name: 'Engineering',
providers: [provide(REACTOR, { useClass: FusionReactor })],
exports: [REACTOR],
});
// Two creates that overlap: both compile before either builds.
const shared = interceptors({
register: [interceptor(AUDIT, { useClass: AuditInterceptor })],
global: [AUDIT],
});
const [first, second] = await Promise.allSettled([
Nexus.create(Engineering, { plugins: [shared] }),
Nexus.create(Engineering, { plugins: [shared] }),
]);
await using _ship = first.status === 'fulfilled' ? first.value : undefined;
const failed = second.status === 'rejected' ? second.reason : undefined;
if (!isNexusError(failed, 'NEXUS_PROVIDER_FAILED')) throw failed;
const pluginError = failed.cause;
if (!isNexusError(pluginError, 'NEXUS_PLUGIN_FAILED')) throw failed;
const inner = pluginError.cause;
// the code of an error, or null
const codeOf = (e: unknown) => (isNexusError(e) ? e.code : null);
const codes = [failed, pluginError, inner].map(codeOf); // -> ['NEXUS_PROVIDER_FAILED', 'NEXUS_PLUGIN_FAILED', 'NEXUS_INTERCEPTORS_SHARED']
console.log(codes);
const lines = isNexusError(inner) ? inner.message.split('\n') : null; // -> ['[NEXUS_INTERCEPTORS_SHARED] this interceptors() plugin is in use by a running container or an unfinished create.', ' Fix: call interceptors() once per container.']
console.log(lines);Try it: save the region as overlap.ts and run npx tsx overlap.ts. It prints the chain of codes as an array, NEXUS_PROVIDER_FAILED, NEXUS_PLUGIN_FAILED, NEXUS_INTERCEPTORS_SHARED, one to a line, then the message of the innermost error as an array of two lines: [NEXUS_INTERCEPTORS_SHARED] this interceptors() plugin is in use by a running container or an unfinished create. and Fix: call interceptors() once per container.
Full text of NEXUS_INTERCEPTORS_SHARED with errors()
NEXUS_INTERCEPTORS_SHARED carries its full text and fix line in the errors list when interceptorsText from @nexusdi/interceptors/text is passed to errors() from @nexusdi/errors, as errors({ text: [interceptorsText] }) in the plugins of Nexus.create. devtools({ text: [interceptorsText] }) words the error the same way.
import { Nexus, Token, defineModule, provide } from '@nexusdi/core';
import { isNexusError } from '@nexusdi/core';
import { errors } from '@nexusdi/errors';
import { interceptor, interceptors } from '@nexusdi/interceptors';
import type { CallContext, Interceptor, Next } from '@nexusdi/interceptors';
import { interceptorsText } from '@nexusdi/interceptors/text';
interface IReactorCore {
output(): number;
}
const REACTOR = new Token<IReactorCore>('ReactorCore');
const AUDIT = new Token<Interceptor>('Audit');
class AuditInterceptor implements Interceptor {
intercept(_call: CallContext, next: Next) {
return next();
}
}
class FusionReactor implements IReactorCore {
output() {
return 1.21;
}
}
const Engineering = defineModule({
name: 'Engineering',
providers: [provide(REACTOR, { useClass: FusionReactor })],
exports: [REACTOR],
});
const shared = interceptors({
register: [interceptor(AUDIT, { useClass: AuditInterceptor })],
global: [AUDIT],
});
const plugins = [errors({ text: [interceptorsText] }), shared];
await using _ship = await Nexus.create(Engineering, { plugins });
const error = await Nexus.create(Engineering, { plugins }).catch(
(caught: unknown) => caught,
);
const inner = isNexusError(error, 'NEXUS_BLUEPRINT_INVALID')
? error.errors[0]
: undefined;
const lines = inner?.message.split('\n'); // -> ['[NEXUS_INTERCEPTORS_SHARED] this interceptors() plugin is in use by a running container or an unfinished create.', ' Fix: call interceptors() once per container.']
console.log(lines);Try it: run npm install @nexusdi/errors@next, save the region as full.ts and run npx tsx full.ts. It prints an array of two lines: [NEXUS_INTERCEPTORS_SHARED] this interceptors() plugin is in use by a running container or an unfinished create. and Fix: call interceptors() once per container.
Fix NEXUS_INTERCEPTORS_SHARED
NEXUS_INTERCEPTORS_SHARED goes away when every container receives its own interceptors() object. Write a function that returns the plugins list and call it once per Nexus.create, or dispose the first container before the second one is created.
import { Nexus, Token, defineModule, provide } from '@nexusdi/core';
import { interceptor, interceptors } from '@nexusdi/interceptors';
import type { CallContext, Interceptor, Next } from '@nexusdi/interceptors';
interface IReactorCore {
output(): number;
}
const REACTOR = new Token<IReactorCore>('ReactorCore');
const AUDIT = new Token<Interceptor>('Audit');
class AuditInterceptor implements Interceptor {
intercept(_call: CallContext, next: Next) {
return next();
}
}
class FusionReactor implements IReactorCore {
output() {
return 1.21;
}
}
const Engineering = defineModule({
name: 'Engineering',
providers: [provide(REACTOR, { useClass: FusionReactor })],
exports: [REACTOR],
});
// One interceptors() call per container.
const plugins = () => [
interceptors({
register: [interceptor(AUDIT, { useClass: AuditInterceptor })],
global: [AUDIT],
}),
];
await using ship = await Nexus.create(Engineering, { plugins: plugins() });
await using simulator = await Nexus.create(Engineering, { plugins: plugins() });
const outputs = [ship.get(REACTOR).output(), simulator.get(REACTOR).output()]; // -> [1.21, 1.21]
console.log(outputs);The fix region differs from the reproduce region in the plugins function, which builds a new interceptors() object for each call, and in what follows: it keeps both containers and prints the output of each.
Try it: save the region as fixed.ts and run npx tsx fixed.ts. It prints [ 1.21, 1.21 ]. Add the line const once = plugins(); above the first Nexus.create and pass once as the plugins option of both calls. 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_INTERCEPTORS_SHARED] and the address of this page. Remove the line and pass plugins() again, and the program prints its line again.
Method Interceptors covers the plugin’s options.
Fields of InterceptorError
code:'NEXUS_INTERCEPTORS_SHARED'.reason:nullfor this code.token:nullfor this code.target:nullfor this code.method:nullfor this code.state:nullfor this code.detail: an empty list for this code.