Skip to content

fix: reset API state on close#1430

Open
Krishan27 wants to merge 3 commits into
open-feature:mainfrom
Krishan27:fix/1374-reset-api-state-on-close
Open

fix: reset API state on close#1430
Krishan27 wants to merge 3 commits into
open-feature:mainfrom
Krishan27:fix/1374-reset-api-state-on-close

Conversation

@Krishan27

@Krishan27 Krishan27 commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Fixes #1374.

Updates the close() shutdown path so it fully resets API-managed state after provider shutdown, including providers, hooks, event handlers, global evaluation context, and transaction-context propagator state.

clearProviders() remains provider-only for backward compatibility, matching maintainer guidance.

@Krishan27 Krishan27 requested review from a team as code owners June 19, 2026 18:00
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ca01efb4-5572-449c-9526-680ae654fee3

📥 Commits

Reviewing files that changed from the base of the PR and between 2a2bc6d and 4549bd0.

📒 Files selected for processing (6)
  • packages/nest/test/open-feature.module.spec.ts
  • packages/server/src/open-feature.ts
  • packages/server/test/open-feature.spec.ts
  • packages/shared/src/open-feature.ts
  • packages/web/src/open-feature.ts
  • packages/web/test/open-feature.spec.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/nest/test/open-feature.module.spec.ts
  • packages/web/src/open-feature.ts
  • packages/server/src/open-feature.ts
  • packages/web/test/open-feature.spec.ts
  • packages/shared/src/open-feature.ts
  • packages/server/test/open-feature.spec.ts

📝 Walkthrough

Walkthrough

close() now resets OpenFeature API state across shared, server, and web implementations. Tests were updated in server, web, and nest to cover provider shutdown, context and handler cleanup, and the reordered Nest injection case.

Changes

Full API state reset on close()

Layer / File(s) Summary
Shared close() teardown and _shutdownAllProviders extraction
packages/shared/src/open-feature.ts
close() now clears hooks, evaluation context, domain-scoped context, client event handler/emitter caches, and all API emitter handlers after awaiting _shutdownAllProviders(). clearProvidersAndSetDefault() now uses _shutdownAllProviders() directly and then resets domain providers and the default ProviderWrapper.
Server close() override and test coverage
packages/server/src/open-feature.ts, packages/server/test/open-feature.spec.ts
Server OpenFeatureAPI adds an async close() override that clears domain-scoped providers, resets the default provider to NOOP_PROVIDER in NOT_READY, and restores the transaction context propagator. Tests cover close() and clearProviders() behavior, including provider lifecycle mocking.
Web close() override and test coverage
packages/web/src/open-feature.ts, packages/web/test/open-feature.spec.ts
Web OpenFeatureAPI adds an async close() override that clears domain-scoped providers and resets the default provider to NOOP_PROVIDER in NOT_READY. clearProviders() no longer clears domain-scoped context. Tests cover close() and clearProviders() behavior.
Nest test reorganization for API state reset
packages/nest/test/open-feature.module.spec.ts
The "without configured providers" client-injection test block is moved later in the suite, after provider-dependent tests, with a comment noting module close resets OpenFeature API state.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Suggested reviewers

  • toddbaert
  • jonathannorris
  • lukas-reining
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: resetting API state on close.
Description check ✅ Passed The description matches the changes and the stated goal of fully resetting shutdown state while keeping clearProviders provider-only.
Linked Issues check ✅ Passed The code changes satisfy #1374 by fully resetting API state on close and preserving clearProviders as provider-only.
Out of Scope Changes check ✅ Passed The changes stay focused on shutdown/reset behavior and related tests, with no obvious unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Comment @coderabbitai help to get the list of available commands.

@Krishan27 Krishan27 force-pushed the fix/1374-reset-api-state-on-close branch from 9ee9e52 to 842e998 Compare June 19, 2026 18:05

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/shared/src/open-feature.ts (1)

422-439: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Deduplicate provider instances before calling onClose().

_shutdownAllProviders() currently closes the default wrapper provider and every domain wrapper provider independently. If the same provider instance is bound in multiple scopes, onClose() is invoked multiple times. That can trigger double-shutdown side effects for non-idempotent providers.

Suggested fix
   private async _shutdownAllProviders(): Promise<void> {
-    try {
-      await this?._defaultProvider.provider?.onClose?.();
-    } catch (err) {
-      this.handleShutdownError(this._defaultProvider.provider, err);
-    }
-
-    const wrappers = Array.from(this._domainScopedProviders);
-
-    await Promise.all(
-      wrappers.map(async ([, wrapper]) => {
-        try {
-          await wrapper?.provider.onClose?.();
-        } catch (err) {
-          this.handleShutdownError(wrapper?.provider, err);
-        }
-      }),
-    );
+    const uniqueProviders = new Set<P>([
+      this._defaultProvider.provider,
+      ...Array.from(this._domainScopedProviders.values()).map((wrapper) => wrapper.provider),
+    ]);
+
+    await Promise.all(
+      Array.from(uniqueProviders).map(async (provider) => {
+        try {
+          await provider?.onClose?.();
+        } catch (err) {
+          this.handleShutdownError(provider, err);
+        }
+      }),
+    );
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/shared/src/open-feature.ts` around lines 422 - 439, In the
`_shutdownAllProviders()` method, the same provider instance can have
`onClose()` called multiple times if it's bound to multiple scopes (default and
domain-scoped). Deduplicate the provider instances by collecting all unique
providers from both the default provider and domain-scoped wrappers into a
single collection (such as a Set), then iterate through the deduplicated
collection once to call `onClose()` on each unique provider instance to avoid
triggering double-shutdown side effects.
🧹 Nitpick comments (1)
packages/web/test/open-feature.spec.ts (1)

308-334: ⚡ Quick win

Add an explicit domain-scoped context assertion for clearProviders().

This block validates global context retention but does not lock the domain-scoped context contract. Adding one test here (either “retained” or “cleared,” per intended behavior) will prevent future ambiguity/regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/web/test/open-feature.spec.ts` around lines 308 - 334, The
clearProviders() test suite in the 'does not clear global hooks', 'does not
clear global evaluation context', and 'does not remove API-level event handlers'
tests validates global-level contract retention but lacks explicit coverage for
domain-scoped context behavior. Add a new test case within the
describe('clearProviders() remains provider-only') block that sets a
domain-specific context, calls clearProviders(), and then explicitly asserts
whether the domain-scoped context is retained or cleared as intended by the API
contract. This prevents future regressions around domain-scoped context
handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/shared/src/open-feature.ts`:
- Around line 422-439: In the `_shutdownAllProviders()` method, the same
provider instance can have `onClose()` called multiple times if it's bound to
multiple scopes (default and domain-scoped). Deduplicate the provider instances
by collecting all unique providers from both the default provider and
domain-scoped wrappers into a single collection (such as a Set), then iterate
through the deduplicated collection once to call `onClose()` on each unique
provider instance to avoid triggering double-shutdown side effects.

---

Nitpick comments:
In `@packages/web/test/open-feature.spec.ts`:
- Around line 308-334: The clearProviders() test suite in the 'does not clear
global hooks', 'does not clear global evaluation context', and 'does not remove
API-level event handlers' tests validates global-level contract retention but
lacks explicit coverage for domain-scoped context behavior. Add a new test case
within the describe('clearProviders() remains provider-only') block that sets a
domain-specific context, calls clearProviders(), and then explicitly asserts
whether the domain-scoped context is retained or cleared as intended by the API
contract. This prevents future regressions around domain-scoped context
handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fdffd2a9-618f-47ff-8731-92f1e997fc7a

📥 Commits

Reviewing files that changed from the base of the PR and between 3c01041 and 9ee9e52.

📒 Files selected for processing (6)
  • packages/nest/test/open-feature.module.spec.ts
  • packages/server/src/open-feature.ts
  • packages/server/test/open-feature.spec.ts
  • packages/shared/src/open-feature.ts
  • packages/web/src/open-feature.ts
  • packages/web/test/open-feature.spec.ts

@Krishan27

Krishan27 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

@MattIPv4 @beeme1mr Thank you for review, @lukas-reining please review and If there is nothing else to edit in this PR we might can merge it and close it. Thank you again.

@lukas-reining lukas-reining left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This looks great beside one thought. Thank you @Krishan27!

Comment thread packages/shared/src/open-feature.ts Outdated
}
}),
);
await this._shutdownAllProviders();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If handleShutdownError in _shutdownAllProviders throws, the state will not be reset.
In clearProvidersAndSetDefault we have catch for this case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@lukas-reining Good catch, thanks! Now I just Wrapped _shutdownAllProviders() in close() with the same try/catch/finally pattern used in clearProvidersAndSetDefault(), so state is always reset even if provider shutdown throws.

Implements spec requirement 1.6.2: close() now fully resets all
API-managed state after provider shutdown, including providers,
global and domain-scoped hooks, event handlers, global evaluation
context, domain-scoped evaluation context, and (server) transaction
context propagator.

clearProviders() remains provider-only for backward compatibility,
as guided by the maintainers.

Fixes open-feature#1374.

Signed-off-by: Krishan Kant Sharma <krishansharma0327@gmail.com>
Signed-off-by: Krishan Kant Sharma <krishansharma0327@gmail.com>
Wrap _shutdownAllProviders() in try/catch/finally so that hooks, context,
event handlers, and emitter are always cleared, matching the pattern already
used in clearProvidersAndSetDefault().

Signed-off-by: Krishan Kant Sharma <krishansharma0327@gmail.com>
@Krishan27 Krishan27 force-pushed the fix/1374-reset-api-state-on-close branch from 2a2bc6d to 4549bd0 Compare June 30, 2026 03:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

shutdown/close doesn't fully reset API state

3 participants