Skip to content

fix(core): proxy provider improvements - optional deps, better error messages, docs caveats#579

Open
Papooch wants to merge 3 commits into
mainfrom
fix/proxy-provider-improvements
Open

fix(core): proxy provider improvements - optional deps, better error messages, docs caveats#579
Papooch wants to merge 3 commits into
mainfrom
fix/proxy-provider-improvements

Conversation

@Papooch
Copy link
Copy Markdown
Owner

@Papooch Papooch commented May 25, 2026

Changes

Support OptionalFactoryDependency in inject arrays (closes #381)

Factory proxy providers now accept { token: T, optional: true } in inject:

ClsModule.forFeatureAsync({
  provide: MY_PROXY,
  inject: [{ token: SomeService, optional: true }],
  useFactory: (svc?: SomeService) => ({ value: svc?.getValue() ?? 'default' }),
});

@Optional() on class proxy constructor params already worked.

Throw on invalid factory return values (closes #375)

Factories returning null, undefined, or a primitive now throw ProxyProviderInvalidReturnTypeException instead of silently producing a broken proxy.

Docs

@Papooch Papooch changed the title fix(core): proxy provider improvements — optional deps, better error messages, docs caveats fix(core): proxy provider improvements - optional deps, better error messages, docs caveats May 25, 2026
@Papooch Papooch force-pushed the fix/proxy-provider-improvements branch from 01184bc to 1eb80bc Compare May 25, 2026 19:47
@Papooch Papooch requested a review from Copilot May 25, 2026 19:49
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves Proxy Provider ergonomics and reliability in nestjs-cls by supporting Nest’s optional factory injection syntax, adding stricter validation of factory return values during proxy resolution, and documenting common lifecycle/scope pitfalls.

Changes:

  • Extend Proxy Provider/Plugin factory inject typing to accept OptionalFactoryDependency (e.g. { token, optional: true }) and add tests for optional injection behavior and ordering.
  • Validate resolved Proxy Provider factory return values and throw ProxyProviderInvalidReturnTypeException for null/undefined/primitives; track “resolved” providers explicitly per CLS context.
  • Update docs with stricter-mode wording and new caveats around bootstrap timing and REQUEST-scoped providers.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/core/test/proxy-providers/for-feature.spec.ts Adds tests for @Optional() in class proxies and OptionalFactoryDependency in factory proxy inject arrays (including ordering).
packages/core/src/lib/proxy-provider/proxy-provider.interfaces.ts Widens inject/dependencies types to include OptionalFactoryDependency.
packages/core/src/lib/proxy-provider/proxy-provider.exceptions.ts Introduces ProxyProviderInvalidReturnTypeException with an explanatory message.
packages/core/src/lib/proxy-provider/proxy-provider-resolver.ts Extracts tokens from optional deps for dependency ordering; adds per-context resolved tracking; throws on invalid factory return values.
packages/core/src/lib/proxy-provider/proxy-provider-manager.spec.ts Adds coverage for invalid return type handling and “don’t re-resolve” behavior.
packages/core/src/lib/plugin/cls-plugin-base.ts Updates plugin hook provider inject typing to accept OptionalFactoryDependency.
docs/docs/03_features-and-use-cases/06_proxy-providers.md Updates strict-mode wording and adds new caveats (bootstrap timing, REQUEST-scoped mixing, primitive returns).
docs/docs/03_features-and-use-cases/04_usage-outside-of-web-request.md Adds a warning about background workers and Proxy Provider availability timing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread docs/docs/03_features-and-use-cases/06_proxy-providers.md Outdated
Comment thread docs/docs/03_features-and-use-cases/04_usage-outside-of-web-request.md Outdated
Papooch and others added 2 commits May 25, 2026 22:00
… arrays

Accepts `{ token: T, optional: true }` in the `inject` array of
`ClsModule.forFeatureAsync` factory providers and in `ClsPluginBase.registerHooks`.

The `extractInjectionToken` helper correctly extracts the bare token from an
`OptionalFactoryDependency` when building the proxy-to-proxy dependency graph,
so resolution ordering is preserved even for optional cross-proxy deps.

`@Optional()` on class proxy provider constructor params already worked via
`moduleRef.create()` — integration tests verify both class and factory cases.

Closes #381
…ve returns

- Track resolved providers in a Set (CLS_PROXY_PROVIDER_RESOLVED_SET) to
  distinguish 'not resolved yet' from 'resolved to undefined/null'
- Throw ProxyProviderInvalidReturnTypeException when factory returns a
  primitive (string, number, boolean, null, undefined)
- Handle OptionalFactoryDependency entries in the dependencies array
- Add tests covering all new error paths and resolution tracking

Closes #375

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Papooch Papooch force-pushed the fix/proxy-provider-improvements branch from 1eb80bc to a614f71 Compare May 25, 2026 20:01
@Papooch Papooch requested a review from Copilot May 25, 2026 20:01
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

packages/core/src/lib/proxy-provider/proxy-provider-resolver.ts:163

  • getAllNeededProviderSymbols only adds direct dependencies of the requested providers. When resolving a subset (e.g. cls.proxy.resolve([B])) and B -> A -> C (transitively), C is never added to resolutionPromisesMap, so A will not actually wait for C (because dependencyPromises.get(C) is undefined) and can resolve out of order. This breaks the “resolve dependencies first” guarantee for selective resolution. Consider computing the full transitive closure of proxy-provider dependencies (DFS/BFS) and/or throwing if a dependency symbol is missing from dependencyPromises.
    private getAllNeededProviderSymbols(providerSymbols: symbol[]) {
        const resolvedSet = this.getOrCreateCurrentResolvedSet();
        return new Set<symbol>(
            providerSymbols
                .filter((providerSymbol) => !resolvedSet.has(providerSymbol))
                .map((providerSymbol) => {
                    const deps =
                        this.proxyProviderDependenciesMap.get(providerSymbol) ??
                        [];
                    return [providerSymbol, ...deps];
                })
                .flat(),
        );
    }

Comment thread packages/core/src/lib/proxy-provider/proxy-provider-resolver.ts
…ped mixing

- Warn that proxy providers are unavailable until onApplicationBootstrap
  (BullMQ and similar workers must not consume in onModuleInit)
- Danger block: never inject Scope.REQUEST providers into proxy provider
  factories; shows before/after with CLS_REQ pattern

Closes #404, relates to #375

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Papooch Papooch force-pushed the fix/proxy-provider-improvements branch from a614f71 to 676c0c9 Compare May 25, 2026 20:18
@Papooch Papooch requested a review from Copilot May 25, 2026 20:18
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comment on lines +154 to +158
.filter(
(providerSymbol) =>
!resolvedSet.has(providerSymbol) &&
!this.cls.has(providerSymbol),
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants