@tstdl/base 0.93.11 → 0.93.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,7 @@ import { assert, isArray, isBoolean, isDefined, isFunction, isNotNull, isNotObje
12
12
  import { setCurrentInjectionContext } from './inject.js';
13
13
  import { afterResolve } from './interfaces.js';
14
14
  import { isClassProvider, isFactoryProvider, isProviderWithInitializer, isTokenProvider, isValueProvider } from './provider.js';
15
+ import { RegistrationError } from './registration.error.js';
15
16
  import { runInResolutionContext } from './resolution.js';
16
17
  import { ResolveChain } from './resolve-chain.js';
17
18
  import { ResolveError } from './resolve.error.js';
@@ -277,7 +278,7 @@ export class Injector {
277
278
  return forwardRef;
278
279
  }
279
280
  if (isUndefined(token)) {
280
- throw new ResolveError('Token is undefined - this might be because of circular dependencies, use alias or forwardRef in this case.', chain);
281
+ throw new ResolveError('Token is undefined. This might be due to a circular dependency. Consider using an alias or forwardRef.', chain);
281
282
  }
282
283
  const registration = (options.skipSelf == true) ? undefined : this.tryGetRegistration(token);
283
284
  if (isDefined(registration)) {
@@ -302,7 +303,7 @@ export class Injector {
302
303
  return forwardRef;
303
304
  }
304
305
  if (isUndefined(token)) {
305
- throw new ResolveError('Token is undefined - this might be because of circular dependencies, use alias or forwardRef in this case.', chain);
306
+ throw new ResolveError('Token is undefined. This might be due to a circular dependency. Consider using an alias or forwardRef.', chain);
306
307
  }
307
308
  const registration = (options.skipSelf == true) ? undefined : this.tryGetRegistration(token);
308
309
  if (isDefined(registration)) {
@@ -333,12 +334,7 @@ export class Injector {
333
334
  const singletonScoped = registration.options.lifecycle == 'singleton';
334
335
  let resolveArgument = argument ?? registration.options.defaultArgument;
335
336
  if (isUndefined(resolveArgument) && isFunction(registration.options.defaultArgumentProvider)) {
336
- try {
337
- resolveArgument = registration.options.defaultArgumentProvider(injector.getResolveContext(resolutionTag, context, chain));
338
- }
339
- catch (error) {
340
- throw new ResolveError('Error in defaultArgumentProvider.', chain, error);
341
- }
337
+ resolveArgument = wrapInResolveError(() => registration.options.defaultArgumentProvider(injector.getResolveContext(resolutionTag, context, chain)), 'Error in defaultArgumentProvider', chain);
342
338
  }
343
339
  const argumentIdentity = resolveArgumentIdentity(registration, resolveArgument, chain);
344
340
  if (resolutionScoped && context.resolutionScopedResolutions.hasFlat(token, argumentIdentity)) {
@@ -350,9 +346,7 @@ export class Injector {
350
346
  else if (singletonScoped && registration.resolutions.has(argumentIdentity)) {
351
347
  return registration.resolutions.get(argumentIdentity);
352
348
  }
353
- const resolutionContext = {
354
- afterResolveRegistrations: [],
355
- };
349
+ const resolutionContext = { afterResolveRegistrations: [] };
356
350
  const value = injector._resolveProvider(resolutionTag, registration, resolveArgument, options, context, resolutionContext, injectionContext, chain);
357
351
  const resolution = {
358
352
  tag: resolutionTag,
@@ -389,23 +383,16 @@ export class Injector {
389
383
  const arg = resolveArgument ?? provider.defaultArgument ?? provider.defaultArgumentProvider?.();
390
384
  injectionContext.argument = arg;
391
385
  if ((provider.useClass.length > 0) && (isUndefined(typeMetadata) || !typeMetadata.data.has(injectableMetadataSymbol))) {
392
- throw new ResolveError(`${provider.useClass.name} has constructor parameters but is not injectable.`, chain);
386
+ throw new ResolveError(`${provider.useClass.name} has constructor parameters but is not decorated with @Injectable.`, chain);
393
387
  }
394
388
  const parameters = (typeMetadata?.parameters ?? []).map((metadata) => this.resolveClassInjection(resolutionTag, context, provider.useClass, metadata, arg, chain));
395
- try {
396
- result = { value: runInResolutionContext(resolutionContext, () => Reflect.construct(provider.useClass, parameters)) };
397
- }
398
- catch (error) {
399
- if (error instanceof ResolveError) {
400
- throw error;
401
- }
402
- throw new ResolveError('Error in class constructor.', chain, error);
403
- }
389
+ const value = wrapInResolveError(() => runInResolutionContext(resolutionContext, () => Reflect.construct(provider.useClass, parameters)), `Error during construction of '${provider.useClass.name}'`, chain);
390
+ result = { value };
404
391
  }
405
- if (isValueProvider(provider)) {
392
+ else if (isValueProvider(provider)) {
406
393
  result = { value: provider.useValue };
407
394
  }
408
- if (isTokenProvider(provider)) {
395
+ else if (isTokenProvider(provider)) {
409
396
  const innerToken = (provider.useToken ?? provider.useTokenProvider());
410
397
  const arg = resolveArgument ?? provider.defaultArgument ?? provider.defaultArgumentProvider?.();
411
398
  injectionContext.argument = arg;
@@ -414,17 +401,13 @@ export class Injector {
414
401
  }
415
402
  result = { value: this._resolve(innerToken, arg, options, context, chain.addToken(innerToken)) };
416
403
  }
417
- if (isFactoryProvider(provider)) {
404
+ else if (isFactoryProvider(provider)) {
418
405
  const arg = resolveArgument ?? provider.defaultArgument ?? provider.defaultArgumentProvider?.();
419
406
  injectionContext.argument = arg;
420
- try {
421
- result = { value: runInResolutionContext(resolutionContext, () => provider.useFactory(arg, this.getResolveContext(resolutionTag, context, chain))) };
422
- }
423
- catch (error) {
424
- throw new ResolveError('Error in provider factory.', chain, error);
425
- }
407
+ const value = wrapInResolveError(() => runInResolutionContext(resolutionContext, () => provider.useFactory(arg, this.getResolveContext(resolutionTag, context, chain))), 'Error in provider factory', chain);
408
+ result = { value };
426
409
  }
427
- if (isUndefined(result)) {
410
+ else {
428
411
  throw new Error('Unsupported provider.');
429
412
  }
430
413
  if (isSyncOrAsyncDisposable(result.value) && !this.#disposableStackRegistrations.has(result.value)) {
@@ -442,15 +425,9 @@ export class Injector {
442
425
  const injectMetadata = metadata.data.tryGet(injectMetadataSymbol) ?? {};
443
426
  const injectToken = (injectMetadata.injectToken ?? metadata.type);
444
427
  if (isDefined(injectMetadata.injectArgumentMapper) && (!this.hasRegistration(injectToken) || isDefined(resolveArgument) || isUndefined(injectToken))) {
445
- return injectMetadata.injectArgumentMapper(resolveArgument);
446
- }
447
- let parameterResolveArgument;
448
- try {
449
- parameterResolveArgument = injectMetadata.forwardArgumentMapper?.(resolveArgument) ?? injectMetadata.resolveArgumentProvider?.(this.getResolveContext(resolutionTag, context, getChain(injectToken)));
450
- }
451
- catch (error) {
452
- throw new ResolveError('Error in parameter argument provider (forwardArgumentMapper or resolveArgumentProvider).', getChain(injectToken), error);
428
+ return wrapInResolveError(() => injectMetadata.injectArgumentMapper(resolveArgument), 'Error in injectArgumentMapper', getChain(injectToken));
453
429
  }
430
+ const parameterResolveArgument = wrapInResolveError(() => injectMetadata.forwardArgumentMapper?.(resolveArgument) ?? injectMetadata.resolveArgumentProvider?.(this.getResolveContext(resolutionTag, context, getChain(injectToken))), 'Error in parameter argument provider (forwardArgumentMapper or resolveArgumentProvider)', getChain(injectToken));
454
431
  const { forwardRef } = injectMetadata;
455
432
  if (isDefined(forwardRef) && isDefined(injectMetadata.mapper)) {
456
433
  const forwardToken = isFunction(forwardRef) ? forwardRef() : isBoolean(forwardRef) ? injectToken : forwardRef;
@@ -459,12 +436,7 @@ export class Injector {
459
436
  const resolveFn = (injectMetadata.resolveAll == true) ? '_resolveAll' : '_resolve';
460
437
  const resolved = this[resolveFn](injectToken, parameterResolveArgument, { optional: injectMetadata.optional, forwardRef, forwardRefTypeHint: injectMetadata.forwardRefTypeHint }, context, getChain(injectToken));
461
438
  if (isDefined(injectMetadata.mapper)) {
462
- try {
463
- return injectMetadata.mapper(resolved);
464
- }
465
- catch (error) {
466
- throw new ResolveError('Error in inject mapper.', getChain(injectToken), error);
467
- }
439
+ return wrapInResolveError(() => injectMetadata.mapper(resolved), 'Error in inject mapper', getChain(injectToken));
468
440
  }
469
441
  return resolved;
470
442
  }
@@ -485,7 +457,7 @@ export class Injector {
485
457
  return values;
486
458
  }
487
459
  getResolveContext(resolutionTag, resolveContext, chain) {
488
- const context = {
460
+ return {
489
461
  resolve: (token, argument, options) => this._resolve(token, argument, options ?? {}, resolveContext, chain.addToken(token)),
490
462
  resolveAll: (token, argument, options) => this._resolveAll(token, argument, options ?? {}, resolveContext, chain.addToken(token)),
491
463
  cancellationSignal: this.#disposeToken,
@@ -494,21 +466,19 @@ export class Injector {
494
466
  return resolveContext.resolutionContextData.get(resolutionTag);
495
467
  },
496
468
  };
497
- return context;
498
469
  }
499
470
  getAfterResolveContext(resolutionTag, resolveContext) {
500
- const context = {
471
+ return {
501
472
  cancellationSignal: this.#disposeToken,
502
473
  addDisposeHandler: this.#addDisposeHandler,
503
474
  get data() {
504
475
  return resolveContext.resolutionContextData.get(resolutionTag);
505
476
  },
506
477
  };
507
- return context;
508
478
  }
509
479
  getInjectionContext(resolveContext, resolveArgument, chain) {
510
480
  let injectIndex = 0;
511
- const context = {
481
+ return {
512
482
  injector: this,
513
483
  argument: resolveArgument,
514
484
  inject: (token, argument, options) => this.resolveInjection(token, argument, options ?? {}, resolveContext, injectIndex++, chain),
@@ -518,7 +488,6 @@ export class Injector {
518
488
  injectAllAsync: async (token, argument, options) => await this.resolveInjectionAllAsync(token, argument, options ?? {}, resolveContext, injectIndex++, chain),
519
489
  injectManyAsync: async (...tokens) => await this.resolveManyAsync(...tokens),
520
490
  };
521
- return context;
522
491
  }
523
492
  assertNotDisposed() {
524
493
  if (this.disposed) {
@@ -533,7 +502,7 @@ function addRegistration(registrations, registration) {
533
502
  if (isClassProvider(registration.provider)) {
534
503
  const injectable = reflectionRegistry.getMetadata(registration.provider.useClass)?.data.has(injectableMetadataSymbol) ?? false;
535
504
  if (!injectable) {
536
- throw new Error(`${registration.provider.useClass.name} is not injectable.`);
505
+ throw new RegistrationError(`${registration.provider.useClass.name} is not decorated with Injectable and cannot be registered.`);
537
506
  }
538
507
  }
539
508
  const multi = registration.options.multi ?? false;
@@ -541,7 +510,8 @@ function addRegistration(registrations, registration) {
541
510
  const hasExistingRegistration = isDefined(existingRegistration);
542
511
  const existingIsMulti = hasExistingRegistration && isArray(existingRegistration);
543
512
  if (hasExistingRegistration && (existingIsMulti != multi)) {
544
- throw new Error('Cannot mix multi and non-multi registrations.');
513
+ const tokenName = getTokenName(registration.token);
514
+ throw new RegistrationError(`Cannot mix multi and non-multi registrations for token: ${tokenName}.`);
545
515
  }
546
516
  if (multi && existingIsMulti) {
547
517
  existingRegistration.push(registration);
@@ -568,29 +538,31 @@ function postProcess(context) {
568
538
  }
569
539
  derefForwardRefs(context);
570
540
  for (const resolution of context.resolutions) {
571
- try {
572
- for (const afterResolveHandler of resolution.afterResolveRegistrations) {
573
- const returnValue = afterResolveHandler(resolution.argument, resolution.afterResolveContext);
574
- throwOnPromise(returnValue, 'registerAfterResolve()', resolution.chain);
575
- }
576
- if (!isTokenProvider(resolution.registration.provider) && isFunction(resolution.value?.[afterResolve])) {
577
- const returnValue = resolution.value[afterResolve](resolution.argument, resolution.afterResolveContext);
578
- throwOnPromise(returnValue, '[afterResolve]', resolution.chain);
579
- }
580
- if (isProviderWithInitializer(resolution.registration.provider)) {
581
- const returnValue = resolution.registration.provider.afterResolve?.(resolution.value, resolution.argument, resolution.afterResolveContext);
582
- throwOnPromise(returnValue, 'provider afterResolve handler', resolution.chain);
583
- }
584
- if (isDefined(resolution.registration.options.afterResolve)) {
585
- const returnValue = resolution.registration.options.afterResolve(resolution.value, resolution.argument, resolution.afterResolveContext);
541
+ const { registration, value, argument, afterResolveContext, afterResolveRegistrations, chain } = resolution;
542
+ const provider = registration.provider;
543
+ for (const afterResolveHandler of afterResolveRegistrations) {
544
+ wrapInResolveError(() => {
545
+ const returnValue = afterResolveHandler(argument, afterResolveContext);
546
+ throwOnPromise(returnValue, 'registerAfterResolve()', chain);
547
+ }, 'Error in registered afterResolve handler', chain);
548
+ }
549
+ if (!isTokenProvider(provider) && isFunction(value?.[afterResolve])) {
550
+ wrapInResolveError(() => {
551
+ const returnValue = value[afterResolve](argument, afterResolveContext);
552
+ throwOnPromise(returnValue, '[afterResolve] method', chain);
553
+ }, 'Error in [afterResolve] method', chain);
554
+ }
555
+ if (isProviderWithInitializer(provider)) {
556
+ wrapInResolveError(() => {
557
+ const returnValue = provider.afterResolve?.(value, argument, afterResolveContext);
558
+ throwOnPromise(returnValue, 'provider afterResolve handler', chain);
559
+ }, 'Error in providers afterResolve handler', chain);
560
+ }
561
+ if (isDefined(registration.options.afterResolve)) {
562
+ wrapInResolveError(() => {
563
+ const returnValue = registration.options.afterResolve(value, argument, afterResolveContext);
586
564
  throwOnPromise(returnValue, 'registration afterResolve handler', resolution.chain);
587
- }
588
- }
589
- catch (error) {
590
- if (error instanceof ResolveError) {
591
- throw error;
592
- }
593
- throw new ResolveError('Error in afterResolve hook.', resolution.chain, error);
565
+ }, `Error in registration afterResolve handler`, chain);
594
566
  }
595
567
  }
596
568
  }
@@ -600,42 +572,31 @@ async function postProcessAsync(context) {
600
572
  }
601
573
  derefForwardRefs(context);
602
574
  for (const resolution of context.resolutions) {
603
- try {
604
- for (const afterResolveHandler of resolution.afterResolveRegistrations) {
605
- await afterResolveHandler(resolution.argument, resolution.afterResolveContext);
606
- }
607
- if (!isTokenProvider(resolution.registration.provider) && isFunction(resolution.value?.[afterResolve])) {
608
- await resolution.value[afterResolve](resolution.argument, resolution.afterResolveContext);
609
- }
610
- if (isProviderWithInitializer(resolution.registration.provider)) {
611
- await resolution.registration.provider.afterResolve?.(resolution.value, resolution.argument, resolution.afterResolveContext);
612
- }
613
- if (isDefined(resolution.registration.options.afterResolve)) {
614
- await resolution.registration.options.afterResolve(resolution.value, resolution.argument, resolution.afterResolveContext);
615
- }
575
+ const { registration, value, argument, afterResolveContext, afterResolveRegistrations, chain } = resolution;
576
+ const provider = registration.provider;
577
+ for (const afterResolveHandler of afterResolveRegistrations) {
578
+ await wrapInResolveErrorAsync(async () => await afterResolveHandler(argument, afterResolveContext), 'Error in registered async afterResolve handler', chain);
616
579
  }
617
- catch (error) {
618
- if (error instanceof ResolveError) {
619
- throw error;
620
- }
621
- throw new ResolveError('Error in async afterResolve hook.', resolution.chain, error);
580
+ if (!isTokenProvider(provider) && isFunction(value?.[afterResolve])) {
581
+ await wrapInResolveErrorAsync(async () => await value[afterResolve](argument, afterResolveContext), 'Error in async [afterResolve] method', chain);
582
+ }
583
+ if (isProviderWithInitializer(provider)) {
584
+ await wrapInResolveErrorAsync(async () => await provider.afterResolve?.(value, argument, afterResolveContext), 'Error in providers async afterResolve handler', chain);
585
+ }
586
+ if (isDefined(registration.options.afterResolve)) {
587
+ await wrapInResolveErrorAsync(() => registration.options.afterResolve(value, argument, afterResolveContext), 'Error in registration async afterResolve handler', chain);
622
588
  }
623
589
  }
624
590
  }
625
591
  function resolveArgumentIdentity(registration, resolveArgument, chain) {
626
592
  if (isDefined(registration.options.argumentIdentityProvider) && ((registration.options.lifecycle == 'resolution') || (registration.options.lifecycle == 'singleton'))) {
627
- try {
628
- return registration.options.argumentIdentityProvider(resolveArgument);
629
- }
630
- catch (error) {
631
- throw new ResolveError('Error in argumentIdentityProvider.', chain, error);
632
- }
593
+ return wrapInResolveError(() => registration.options.argumentIdentityProvider(resolveArgument), 'Error in argumentIdentityProvider', chain);
633
594
  }
634
595
  return resolveArgument;
635
596
  }
636
597
  function setResolving(token, context, chain) {
637
598
  if (context.resolving.has(token)) {
638
- throw new ResolveError('Circular dependency to itself detected. Please check your registrations and providers. ForwardRef might be a solution.', chain);
599
+ throw new ResolveError('Circular dependency detected. Use forwardRef for circular dependencies between classes.', chain);
639
600
  }
640
601
  context.resolving.add(token);
641
602
  }
@@ -644,12 +605,12 @@ function deleteResolving(token, context) {
644
605
  }
645
606
  function throwOnPromise(value, type, chain) {
646
607
  if (isPromise(value)) {
647
- throw new ResolveError(`Cannot evaluate async ${type} in synchronous resolve, use resolveAsync() instead.`, chain);
608
+ throw new ResolveError(`Cannot evaluate async ${type} in a synchronous resolve. Use resolveAsync() instead.`, chain);
648
609
  }
649
610
  }
650
611
  function checkOverflow(chain, context) {
651
612
  if ((chain.length > 100) || (++context.resolves > 7500)) {
652
- throw new ResolveError('Resolve stack overflow. This can happen on circular dependencies with transient lifecycles and self reference. Use scoped or singleton lifecycle or forwardRef instead.', chain);
613
+ throw new ResolveError('Resolve stack overflow. This may indicate a circular dependency with transient lifecycles. Consider using a scoped or singleton lifecycle, or forwardRef.', chain);
653
614
  }
654
615
  }
655
616
  function derefForwardRefs(context) {
@@ -658,10 +619,31 @@ function derefForwardRefs(context) {
658
619
  continue;
659
620
  }
660
621
  for (const [key, value] of objectEntries(resolution.value)) {
661
- if (!context.forwardRefs.has(value)) {
662
- continue;
622
+ if (context.forwardRefs.has(value)) {
623
+ resolution.value[key] = ForwardRef.deref(value);
663
624
  }
664
- resolution.value[key] = ForwardRef.deref(value);
665
625
  }
666
626
  }
667
627
  }
628
+ function wrapInResolveError(action, errorMessage, chain) {
629
+ try {
630
+ return action();
631
+ }
632
+ catch (error) {
633
+ if (error instanceof ResolveError) {
634
+ throw error;
635
+ }
636
+ throw new ResolveError(errorMessage, chain, error);
637
+ }
638
+ }
639
+ async function wrapInResolveErrorAsync(action, errorMessage, chain) {
640
+ try {
641
+ return await action();
642
+ }
643
+ catch (error) {
644
+ if (error instanceof ResolveError) {
645
+ throw error;
646
+ }
647
+ throw new ResolveError(errorMessage, chain, error);
648
+ }
649
+ }
@@ -0,0 +1,8 @@
1
+ import { CustomError } from '../errors/custom.error.js';
2
+ /**
3
+ * Represents an error that occurs during the provider registration phase.
4
+ * This indicates a configuration or setup issue, rather than a runtime resolution failure.
5
+ */
6
+ export declare class RegistrationError extends CustomError {
7
+ constructor(message: string);
8
+ }
@@ -0,0 +1,11 @@
1
+ import { CustomError } from '../errors/custom.error.js';
2
+ /**
3
+ * Represents an error that occurs during the provider registration phase.
4
+ * This indicates a configuration or setup issue, rather than a runtime resolution failure.
5
+ */
6
+ export class RegistrationError extends CustomError {
7
+ constructor(message) {
8
+ super({ message });
9
+ this.name = 'RegistrationError';
10
+ }
11
+ }
@@ -1,12 +1,9 @@
1
1
  import { CustomError } from '../errors/custom.error.js';
2
- import { isDefined } from '../utils/type-guards.js';
3
2
  export class ResolveError extends CustomError {
4
3
  constructor(message, chain, cause) {
5
- const causeMessage = isDefined(cause) ? `\n cause: ${cause.message}` : '';
6
4
  super({
7
- message: `${message}${causeMessage}\n chain: ${chain.format(15)}`,
5
+ message: `${message}\n chain: ${chain.format(15)}`,
8
6
  cause,
9
- stack: cause?.stack,
10
7
  });
11
8
  }
12
9
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tstdl/base",
3
- "version": "0.93.11",
3
+ "version": "0.93.12",
4
4
  "author": "Patrick Hein",
5
5
  "publishConfig": {
6
6
  "access": "public"