Commit 1231328
committed
Refactor OpenFeature Java SDK to separate API from implementation
- Created multi-module Maven structure with openfeature-api and openfeature-sdk modules
- Moved core interfaces and data types to API module for clean separation
- Implemented ServiceLoader pattern for automatic SDK discovery and loading
- Created focused interfaces replacing monolithic OpenFeatureAdvanced:
* OpenFeatureCore - basic operations
* OpenFeatureHooks - hook management
* OpenFeatureContext - evaluation context
* OpenFeatureEventHandling - provider events
* OpenFeatureTransactionContext - transaction context
* OpenFeatureLifecycle - shutdown operations
- Moved NoOp implementations to internal.noop package for better encapsulation
- Created EventProvider interface in API with abstract class in SDK for backward compatibility
- Updated HookContext initialization to use builder pattern throughout tests
- Migrated tests to appropriate modules (API vs SDK concerns)
- Fixed classpath and dependency issues for proper module separation
- Updated imports and references to use API interfaces where appropriate
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
diff --git c/benchmark.txt i/benchmark.txt
index e43e684..065a2c5 100644
--- c/benchmark.txt
+++ i/benchmark.txt
@@ -1,5 +1,5 @@
[INFO] Scanning for projects...
-[INFO]
+[INFO]
[INFO] ------------------------< dev.openfeature:sdk >-------------------------
[INFO] Building OpenFeature Java SDK 1.12.1
[INFO] from pom.xml
@@ -7,21 +7,21 @@
[WARNING] Parameter 'encoding' is unknown for plugin 'maven-checkstyle-plugin:3.5.0:check (validate)'
[WARNING] Parameter 'encoding' is unknown for plugin 'maven-checkstyle-plugin:3.5.0:check (validate)'
[WARNING] Parameter 'encoding' is unknown for plugin 'maven-checkstyle-plugin:3.5.0:check (validate)'
-[INFO]
+[INFO]
[INFO] --- clean:3.2.0:clean (default-clean) @ sdk ---
[INFO] Deleting /home/todd/git/java-sdk/target
-[INFO]
+[INFO]
[INFO] --- checkstyle:3.5.0:check (validate) @ sdk ---
[INFO] Starting audit...
Audit done.
[INFO] You have 0 Checkstyle violations.
-[INFO]
+[INFO]
[INFO] --- jacoco:0.8.12:prepare-agent (prepare-agent) @ sdk ---
[INFO] surefireArgLine set to -javaagent:/home/todd/.m2/repository/org/jacoco/org.jacoco.agent/0.8.12/org.jacoco.agent-0.8.12-runtime.jar=destfile=/home/todd/git/java-sdk/target/coverage-reports/jacoco-ut.exec
-[INFO]
+[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ sdk ---
[INFO] skip non existing resourceDirectory /home/todd/git/java-sdk/src/main/resources
-[INFO]
+[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ sdk ---
[INFO] Recompiling the module because of changed source code.
[INFO] Compiling 65 source files with javac [debug target 1.8] to target/classes
@@ -44,24 +44,24 @@ Audit done.
[INFO] /home/todd/git/java-sdk/src/main/java/dev/openfeature/sdk/NoOpProvider.java: Recompile with -Xlint:deprecation for details.
[INFO] /home/todd/git/java-sdk/src/main/java/dev/openfeature/sdk/Value.java: Some input files use unchecked or unsafe operations.
[INFO] /home/todd/git/java-sdk/src/main/java/dev/openfeature/sdk/Value.java: Recompile with -Xlint:unchecked for details.
-[INFO]
+[INFO]
[INFO] --- checkstyle:3.5.0:check (validate) @ sdk ---
[INFO] Starting audit...
Audit done.
[INFO] You have 0 Checkstyle violations.
-[INFO]
+[INFO]
[INFO] --- jacoco:0.8.12:prepare-agent (prepare-agent) @ sdk ---
[INFO] surefireArgLine set to -javaagent:/home/todd/.m2/repository/org/jacoco/org.jacoco.agent/0.8.12/org.jacoco.agent-0.8.12-runtime.jar=destfile=/home/todd/git/java-sdk/target/coverage-reports/jacoco-ut.exec
-[INFO]
+[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ sdk ---
[INFO] skip non existing resourceDirectory /home/todd/git/java-sdk/src/main/resources
-[INFO]
+[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ sdk ---
[INFO] Nothing to compile - all classes are up to date.
-[INFO]
+[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ sdk ---
[INFO] Copying 2 resources from src/test/resources to target/test-classes
-[INFO]
+[INFO]
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ sdk ---
[INFO] Recompiling the module because of changed dependency.
[INFO] Compiling 52 source files with javac [debug target 1.8] to target/test-classes
@@ -80,29 +80,29 @@ Audit done.
[INFO] /home/todd/git/java-sdk/src/test/java/dev/openfeature/sdk/EventsTest.java: Recompile with -Xlint:deprecation for details.
[INFO] /home/todd/git/java-sdk/src/test/java/dev/openfeature/sdk/HookSpecTest.java: Some input files use unchecked or unsafe operations.
[INFO] /home/todd/git/java-sdk/src/test/java/dev/openfeature/sdk/HookSpecTest.java: Recompile with -Xlint:unchecked for details.
-[INFO]
+[INFO]
[INFO] >>> jmh:0.2.2:benchmark (default-cli) > process-test-resources @ sdk >>>
-[INFO]
+[INFO]
[INFO] --- checkstyle:3.5.0:check (validate) @ sdk ---
[INFO] Starting audit...
Audit done.
[INFO] You have 0 Checkstyle violations.
-[INFO]
+[INFO]
[INFO] --- jacoco:0.8.12:prepare-agent (prepare-agent) @ sdk ---
[INFO] surefireArgLine set to -javaagent:/home/todd/.m2/repository/org/jacoco/org.jacoco.agent/0.8.12/org.jacoco.agent-0.8.12-runtime.jar=destfile=/home/todd/git/java-sdk/target/coverage-reports/jacoco-ut.exec
-[INFO]
+[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ sdk ---
[INFO] skip non existing resourceDirectory /home/todd/git/java-sdk/src/main/resources
-[INFO]
+[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ sdk ---
[INFO] Nothing to compile - all classes are up to date.
-[INFO]
+[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ sdk ---
[INFO] Copying 2 resources from src/test/resources to target/test-classes
-[INFO]
+[INFO]
[INFO] <<< jmh:0.2.2:benchmark (default-cli) < process-test-resources @ sdk <<<
-[INFO]
-[INFO]
+[INFO]
+[INFO]
[INFO] --- jmh:0.2.2:benchmark (default-cli) @ sdk ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 52 source files to /home/todd/git/java-sdk/target/test-classes
@@ -150,7 +150,7 @@ Iteration 1: num #instances #bytes class name (module)
19: 149 1884376 [Ljdk.internal.vm.FillerElement; (java.base@21.0.4)
20: 56476 1807232 java.util.ArrayList$Itr (java.base@21.0.4)
21: 37481 1799088 dev.openfeature.sdk.FlagEvaluationDetails$FlagEvaluationDetailsBuilder
- 22: 100001 1600016 dev.openfeature.sdk.NoOpProvider$$Lambda/0x000076e79c02fa78
+ 22: 100001 1600016 dev.openfeature.api.NoOpProvider$$Lambda/0x000076e79c02fa78
23: 50000 1600000 [Ldev.openfeature.sdk.EvaluationContext;
24: 50000 1600000 [Ljava.util.List; (java.base@21.0.4)
25: 100000 1600000 dev.openfeature.sdk.OpenFeatureClient$$Lambda/0x000076e79c082800
diff --git c/openfeature-api/pom.xml i/openfeature-api/pom.xml
index 3e160ab..a6873a8 100644
--- c/openfeature-api/pom.xml
+++ i/openfeature-api/pom.xml
@@ -42,7 +42,7 @@
<version>4.8.6</version>
<scope>provided</scope>
</dependency>
-
+
<!-- Test dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
@@ -77,7 +77,39 @@
</archive>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <version>0.8.13</version>
+ <executions>
+ <execution>
+ <id>jacoco-check</id>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ <configuration>
+ <dataFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</dataFile>
+ <excludes>
+ <exclude>dev/openfeature/api/exceptions/**</exclude>
+ <exclude>dev/openfeature/api/internal/**</exclude>
+ </excludes>
+ <rules>
+ <rule>
+ <element>PACKAGE</element>
+ <limits>
+ <limit>
+ <counter>LINE</counter>
+ <value>COVEREDRATIO</value>
+ <minimum>0.3</minimum>
+ </limit>
+ </limits>
+ </rule>
+ </rules>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
-</project>
\ No newline at end of file
+</project>
diff --git c/openfeature-api/src/lombok.config i/openfeature-api/src/lombok.config
new file mode 100644
index 0000000..ec3b056
--- /dev/null
+++ i/openfeature-api/src/lombok.config
@@ -0,0 +1,2 @@
+lombok.addLombokGeneratedAnnotation = true
+lombok.extern.findbugs.addSuppressFBWarnings = true
diff --git c/openfeature-sdk/src/main/java/dev/openfeature/sdk/Awaitable.java i/openfeature-api/src/main/java/dev/openfeature/api/Awaitable.java
similarity index 97%
rename from openfeature-sdk/src/main/java/dev/openfeature/sdk/Awaitable.java
rename to openfeature-api/src/main/java/dev/openfeature/api/Awaitable.java
index 7d5f477..ad2a109 100644
--- c/openfeature-sdk/src/main/java/dev/openfeature/sdk/Awaitable.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/Awaitable.java
@@ -1,4 +1,4 @@
-package dev.openfeature.sdk;
+package dev.openfeature.api;
/**
* A class to help with synchronization by allowing the optional awaiting of the associated action.
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/EvaluationContext.java i/openfeature-api/src/main/java/dev/openfeature/api/EvaluationContext.java
index 64aae73..39ca965 100644
--- c/openfeature-api/src/main/java/dev/openfeature/api/EvaluationContext.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/EvaluationContext.java
@@ -12,7 +12,7 @@ import java.util.function.Function;
public interface EvaluationContext extends Structure {
String TARGETING_KEY = "targetingKey";
-
+
/**
* Empty evaluation context for use as a default.
*/
diff --git c/openfeature-sdk/src/main/java/dev/openfeature/sdk/EvaluationEvent.java i/openfeature-api/src/main/java/dev/openfeature/api/EvaluationEvent.java
similarity index 93%
rename from openfeature-sdk/src/main/java/dev/openfeature/sdk/EvaluationEvent.java
rename to openfeature-api/src/main/java/dev/openfeature/api/EvaluationEvent.java
index f92e24d..0de8e05 100644
--- c/openfeature-sdk/src/main/java/dev/openfeature/sdk/EvaluationEvent.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/EvaluationEvent.java
@@ -1,4 +1,4 @@
-package dev.openfeature.sdk;
+package dev.openfeature.api;
import java.util.HashMap;
import java.util.Map;
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/EventDetails.java i/openfeature-api/src/main/java/dev/openfeature/api/EventDetails.java
index 9c9a2f5..7500dbb 100644
--- c/openfeature-api/src/main/java/dev/openfeature/api/EventDetails.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/EventDetails.java
@@ -11,13 +11,32 @@ import lombok.experimental.SuperBuilder;
@Data
@SuperBuilder(toBuilder = true)
public class EventDetails extends ProviderEventDetails {
+ /** The domain associated with this event. */
private String domain;
+
+ /** The name of the provider that generated this event. */
private String providerName;
- public static EventDetails fromProviderEventDetails(ProviderEventDetails providerEventDetails, String providerName) {
+ /**
+ * Create EventDetails from ProviderEventDetails with provider name.
+ *
+ * @param providerEventDetails the provider event details
+ * @param providerName the name of the provider
+ * @return EventDetails instance
+ */
+ public static EventDetails fromProviderEventDetails(
+ ProviderEventDetails providerEventDetails, String providerName) {
return fromProviderEventDetails(providerEventDetails, providerName, null);
}
+ /**
+ * Create EventDetails from ProviderEventDetails with provider name and domain.
+ *
+ * @param providerEventDetails the provider event details
+ * @param providerName the name of the provider
+ * @param domain the domain associated with the event
+ * @return EventDetails instance
+ */
public static EventDetails fromProviderEventDetails(
ProviderEventDetails providerEventDetails, String providerName, String domain) {
return builder()
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/EventProvider.java i/openfeature-api/src/main/java/dev/openfeature/api/EventProvider.java
new file mode 100644
index 0000000..e867526
--- /dev/null
+++ i/openfeature-api/src/main/java/dev/openfeature/api/EventProvider.java
@@ -0,0 +1,64 @@
+package dev.openfeature.api;
+
+/**
+ * Interface for feature providers that support real-time events.
+ * Providers can implement this interface to emit events about flag changes,
+ * provider state changes, and other configuration updates.
+ *
+ * @see FeatureProvider
+ */
+public interface EventProvider extends FeatureProvider {
+
+ /**
+ * Emit the specified {@link ProviderEvent}.
+ *
+ * @param event The event type
+ * @param details The details of the event
+ * @return An {@link Awaitable} that can be used to wait for event processing completion
+ */
+ Awaitable emit(ProviderEvent event, ProviderEventDetails details);
+
+ /**
+ * Emit a {@link ProviderEvent#PROVIDER_READY} event.
+ * Shorthand for {@link #emit(ProviderEvent, ProviderEventDetails)}
+ *
+ * @param details The details of the event
+ * @return An {@link Awaitable} that can be used to wait for event processing completion
+ */
+ default Awaitable emitProviderReady(ProviderEventDetails details) {
+ return emit(ProviderEvent.PROVIDER_READY, details);
+ }
+
+ /**
+ * Emit a {@link ProviderEvent#PROVIDER_CONFIGURATION_CHANGED} event.
+ * Shorthand for {@link #emit(ProviderEvent, ProviderEventDetails)}
+ *
+ * @param details The details of the event
+ * @return An {@link Awaitable} that can be used to wait for event processing completion
+ */
+ default Awaitable emitProviderConfigurationChanged(ProviderEventDetails details) {
+ return emit(ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, details);
+ }
+
+ /**
+ * Emit a {@link ProviderEvent#PROVIDER_STALE} event.
+ * Shorthand for {@link #emit(ProviderEvent, ProviderEventDetails)}
+ *
+ * @param details The details of the event
+ * @return An {@link Awaitable} that can be used to wait for event processing completion
+ */
+ default Awaitable emitProviderStale(ProviderEventDetails details) {
+ return emit(ProviderEvent.PROVIDER_STALE, details);
+ }
+
+ /**
+ * Emit a {@link ProviderEvent#PROVIDER_ERROR} event.
+ * Shorthand for {@link #emit(ProviderEvent, ProviderEventDetails)}
+ *
+ * @param details The details of the event
+ * @return An {@link Awaitable} that can be used to wait for event processing completion
+ */
+ default Awaitable emitProviderError(ProviderEventDetails details) {
+ return emit(ProviderEvent.PROVIDER_ERROR, details);
+ }
+}
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/FeatureProvider.java i/openfeature-api/src/main/java/dev/openfeature/api/FeatureProvider.java
index 6564a4d..ab86447 100644
--- c/openfeature-api/src/main/java/dev/openfeature/api/FeatureProvider.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/FeatureProvider.java
@@ -6,7 +6,7 @@ import java.util.List;
/**
* The interface implemented by upstream flag providers to resolve flags for
* their service. If you want to support realtime events with your provider, you
- * should extend the EventProvider class from the SDK module
+ * should implement {@link EventProvider}
*/
public interface FeatureProvider {
Metadata getMetadata();
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/NoOpOpenFeatureAPI.java i/openfeature-api/src/main/java/dev/openfeature/api/NoOpOpenFeatureAPI.java
deleted file mode 100644
index 48b5176..0000000
--- c/openfeature-api/src/main/java/dev/openfeature/api/NoOpOpenFeatureAPI.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package dev.openfeature.api;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * No-operation implementation of OpenFeatureAPI that provides safe defaults.
- * Used as a fallback when no actual implementation is available via ServiceLoader.
- * All operations are safe no-ops that won't affect application functionality.
- */
-public class NoOpOpenFeatureAPI extends OpenFeatureAPI {
-
- private static final NoOpClient NO_OP_CLIENT = new NoOpClient();
-
- @Override
- public Client getClient() {
- return NO_OP_CLIENT;
- }
-
- @Override
- public Client getClient(String domain) {
- return NO_OP_CLIENT;
- }
-
- @Override
- public Client getClient(String domain, String version) {
- return NO_OP_CLIENT;
- }
-
- @Override
- public void setProvider(FeatureProvider provider) {
- // No-op - silently ignore
- }
-
- @Override
- public void setProvider(String domain, FeatureProvider provider) {
- // No-op - silently ignore
- }
-
- @Override
- public Metadata getProviderMetadata() {
- return () -> "No-op Provider";
- }
-
- @Override
- public Metadata getProviderMetadata(String domain) {
- return getProviderMetadata();
- }
-
- @Override
- public void addHooks(Hook... hooks) {
- // No-op - silently ignore
- }
-
- @Override
- public List<Hook> getHooks() {
- return Collections.emptyList();
- }
-
- @Override
- public void clearHooks() {
- // No-op - nothing to clear
- }
-
- @Override
- public OpenFeatureAPI setEvaluationContext(EvaluationContext evaluationContext) {
- return this; // No-op - return self for chaining
- }
-
- @Override
- public EvaluationContext getEvaluationContext() {
- return EvaluationContext.EMPTY;
- }
-
- // Implementation of OpenFeatureEventHandling interface
-
- @Override
- public void addHandler(String domain, ProviderEvent event, Consumer<EventDetails> handler) {
- // No-op - silently ignore
- }
-
- @Override
- public void removeHandler(String domain, ProviderEvent event, Consumer<EventDetails> handler) {
- // No-op - silently ignore
- }
-
-}
\ No newline at end of file
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAPI.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAPI.java
index 872f030..a18028e 100644
--- c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAPI.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAPI.java
@@ -1,18 +1,28 @@
package dev.openfeature.api;
+import dev.openfeature.api.internal.noop.NoOpOpenFeatureAPI;
import java.util.ServiceLoader;
-import java.util.function.Consumer;
/**
* Main abstract class that combines all OpenFeature interfaces.
* Uses ServiceLoader pattern to automatically discover and load implementations.
* This allows for multiple SDK implementations with priority-based selection.
+ *
+ * <p>Implements all OpenFeature interface facets:
+ * - Core operations (client management, provider configuration)
+ * - Hook management (global hook configuration)
+ * - Context management (global evaluation context)
+ * - Event handling (provider event registration and management)
+ * - Transaction context (transaction-scoped context propagation)
+ * - Lifecycle management (cleanup and shutdown)
*/
-public abstract class OpenFeatureAPI implements
- OpenFeatureCore,
- OpenFeatureHooks,
- OpenFeatureContext,
- OpenFeatureEventHandling {
+public abstract class OpenFeatureAPI
+ implements OpenFeatureCore,
+ OpenFeatureHooks,
+ OpenFeatureContext,
+ OpenFeatureEventHandling,
+ OpenFeatureTransactionContext,
+ OpenFeatureLifecycle {
private static volatile OpenFeatureAPI instance;
private static final Object lock = new Object();
@@ -20,7 +30,7 @@ public abstract class OpenFeatureAPI implements
/**
* Gets the singleton OpenFeature API instance.
* Uses ServiceLoader to automatically discover and load the best available implementation.
- *
+ *
* @return The singleton instance
*/
public static OpenFeatureAPI getInstance() {
@@ -38,12 +48,11 @@ public abstract class OpenFeatureAPI implements
* Load the best available OpenFeature implementation using ServiceLoader.
* Implementations are selected based on priority, with higher priorities taking precedence.
* If no implementation is available, returns a no-op implementation.
- *
+ *
* @return the loaded OpenFeature API implementation
*/
private static OpenFeatureAPI loadImplementation() {
- ServiceLoader<OpenFeatureAPIProvider> loader =
- ServiceLoader.load(OpenFeatureAPIProvider.class);
+ ServiceLoader<OpenFeatureAPIProvider> loader = ServiceLoader.load(OpenFeatureAPIProvider.class);
OpenFeatureAPIProvider bestProvider = null;
int highestPriority = Integer.MIN_VALUE;
@@ -57,8 +66,8 @@ public abstract class OpenFeatureAPI implements
}
} catch (Exception e) {
// Log but continue - don't let one bad provider break everything
- System.err.println("Failed to get priority from provider " +
- provider.getClass().getName() + ": " + e.getMessage());
+ System.err.println("Failed to get priority from provider "
+ + provider.getClass().getName() + ": " + e.getMessage());
}
}
@@ -66,8 +75,8 @@ public abstract class OpenFeatureAPI implements
try {
return bestProvider.createAPI();
} catch (Exception e) {
- System.err.println("Failed to create API from provider " +
- bestProvider.getClass().getName() + ": " + e.getMessage());
+ System.err.println("Failed to create API from provider "
+ + bestProvider.getClass().getName() + ": " + e.getMessage());
// Fall through to no-op
}
}
@@ -84,7 +93,4 @@ public abstract class OpenFeatureAPI implements
instance = null;
}
}
-
-
- // All methods from the implemented interfaces are abstract and must be implemented by concrete classes
-}
\ No newline at end of file
+}
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAPIProvider.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAPIProvider.java
index 8246360..99442e7 100644
--- c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAPIProvider.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAPIProvider.java
@@ -8,7 +8,7 @@ package dev.openfeature.api;
public interface OpenFeatureAPIProvider {
/**
* Create an OpenFeature API implementation.
- *
+ *
* @return the API implementation
*/
OpenFeatureAPI createAPI();
@@ -16,10 +16,10 @@ public interface OpenFeatureAPIProvider {
/**
* Priority for this provider. Higher values take precedence.
* This allows multiple implementations to coexist with clear precedence rules.
- *
+ *
* @return priority value (default: 0)
*/
default int getPriority() {
return 0;
}
-}
\ No newline at end of file
+}
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAdvanced.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAdvanced.java
deleted file mode 100644
index cbd7c85..0000000
--- c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureAdvanced.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package dev.openfeature.api;
-
-import java.util.function.Consumer;
-
-/**
- * Advanced/SDK-specific interface for OpenFeature operations.
- * Provides lifecycle management and event handling capabilities.
- * Typically only implemented by full SDK implementations.
- */
-public interface OpenFeatureAdvanced {
- /**
- * Shut down and reset the current status of OpenFeature API.
- * This call cleans up all active providers and attempts to shut down internal
- * event handling mechanisms.
- * Once shut down is complete, API is reset and ready to use again.
- */
- void shutdown();
-
- /**
- * Register an event handler for when a provider becomes ready.
- *
- * @param handler Consumer to handle the event
- * @return api instance for method chaining
- */
- OpenFeatureAPI onProviderReady(Consumer<EventDetails> handler);
-
- /**
- * Register an event handler for when a provider's configuration changes.
- *
- * @param handler Consumer to handle the event
- * @return api instance for method chaining
- */
- OpenFeatureAPI onProviderConfigurationChanged(Consumer<EventDetails> handler);
-
- /**
- * Register an event handler for when a provider becomes stale.
- *
- * @param handler Consumer to handle the event
- * @return api instance for method chaining
- */
- OpenFeatureAPI onProviderStale(Consumer<EventDetails> handler);
-
- /**
- * Register an event handler for when a provider encounters an error.
- *
- * @param handler Consumer to handle the event
- * @return api instance for method chaining
- */
- OpenFeatureAPI onProviderError(Consumer<EventDetails> handler);
-
- /**
- * Register an event handler for a specific provider event.
- *
- * @param event the provider event to listen for
- * @param handler Consumer to handle the event
- * @return api instance for method chaining
- */
- OpenFeatureAPI on(ProviderEvent event, Consumer<EventDetails> handler);
-
- /**
- * Remove an event handler for a specific provider event.
- *
- * @param event the provider event to stop listening for
- * @param handler the handler to remove
- * @return api instance for method chaining
- */
- OpenFeatureAPI removeHandler(ProviderEvent event, Consumer<EventDetails> handler);
-}
\ No newline at end of file
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureContext.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureContext.java
index 3339c8e..9de205b 100644
--- c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureContext.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureContext.java
@@ -19,4 +19,4 @@ public interface OpenFeatureContext {
* @return evaluation context
*/
EvaluationContext getEvaluationContext();
-}
\ No newline at end of file
+}
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureCore.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureCore.java
index ef4d40e..22254e8 100644
--- c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureCore.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureCore.java
@@ -1,5 +1,7 @@
package dev.openfeature.api;
+import dev.openfeature.api.exceptions.OpenFeatureError;
+
/**
* Core interface for basic OpenFeature operations.
* Provides client management and provider configuration.
@@ -42,7 +44,7 @@ public interface OpenFeatureCore {
/**
* Set the default provider.
- *
+ *
* @param provider the provider to set as default
*/
void setProvider(FeatureProvider provider);
@@ -55,6 +57,42 @@ public interface OpenFeatureCore {
*/
void setProvider(String domain, FeatureProvider provider);
+ /**
+ * Sets the default provider and waits for its initialization to complete.
+ *
+ * <p>Note: If the provider fails during initialization, an {@link OpenFeatureError} will be thrown.
+ * It is recommended to wrap this call in a try-catch block to handle potential initialization failures gracefully.
+ *
+ * @param provider the {@link FeatureProvider} to set as the default.
+ * @throws OpenFeatureError if the provider fails during initialization.
+ */
+ void setProviderAndWait(FeatureProvider provider) throws OpenFeatureError;
+
+ /**
+ * Add a provider for a domain and wait for initialization to finish.
+ *
+ * <p>Note: If the provider fails during initialization, an {@link OpenFeatureError} will be thrown.
+ * It is recommended to wrap this call in a try-catch block to handle potential initialization failures gracefully.
+ *
+ * @param domain The domain to bind the provider to.
+ * @param provider The provider to set.
+ * @throws OpenFeatureError if the provider fails during initialization.
+ */
+ void setProviderAndWait(String domain, FeatureProvider provider) throws OpenFeatureError;
+
+ /**
+ * Return the default provider.
+ */
+ FeatureProvider getProvider();
+
+ /**
+ * Fetch a provider for a domain. If not found, return the default.
+ *
+ * @param domain The domain to look for.
+ * @return A named {@link FeatureProvider}
+ */
+ FeatureProvider getProvider(String domain);
+
/**
* Get metadata about the default provider.
*
@@ -70,4 +108,4 @@ public interface OpenFeatureCore {
* @return the provider metadata
*/
Metadata getProviderMetadata(String domain);
-}
\ No newline at end of file
+}
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureEventHandling.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureEventHandling.java
index 336f7d9..20c2f8f 100644
--- c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureEventHandling.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureEventHandling.java
@@ -3,30 +3,58 @@ package dev.openfeature.api;
import java.util.function.Consumer;
/**
- * Interface for advanced event handling capabilities.
- * This interface provides domain-specific event handler management
- * which is typically used by SDK implementations but not required
- * for basic API usage.
+ * Interface for provider event handling operations.
+ * Provides event registration and management for provider state changes,
+ * configuration updates, and other provider lifecycle events.
*/
public interface OpenFeatureEventHandling {
-
/**
- * Add event handlers for domain-specific provider events.
- * This method is used by SDK implementations to manage client-level event handlers.
- *
- * @param domain the domain for which to add the handler
- * @param event the provider event to listen for
- * @param handler the event handler to add
+ * Register an event handler for when a provider becomes ready.
+ *
+ * @param handler Consumer to handle the event
+ * @return api instance for method chaining
*/
- void addHandler(String domain, ProviderEvent event, Consumer<EventDetails> handler);
-
+ OpenFeatureAPI onProviderReady(Consumer<EventDetails> handler);
+
/**
- * Remove event handlers for domain-specific provider events.
- * This method is used by SDK implementations to manage client-level event handlers.
- *
- * @param domain the domain for which to remove the handler
- * @param event the provider event to stop listening for
- * @param handler the event handler to remove
+ * Register an event handler for when a provider's configuration changes.
+ *
+ * @param handler Consumer to handle the event
+ * @return api instance for method chaining
*/
- void removeHandler(String domain, ProviderEvent event, Consumer<EventDetails> handler);
-}
\ No newline at end of file
+ OpenFeatureAPI onProviderConfigurationChanged(Consumer<EventDetails> handler);
+
+ /**
+ * Register an event handler for when a provider becomes stale.
+ *
+ * @param handler Consumer to handle the event
+ * @return api instance for method chaining
+ */
+ OpenFeatureAPI onProviderStale(Consumer<EventDetails> handler);
+
+ /**
+ * Register an event handler for when a provider encounters an error.
+ *
+ * @param handler Consumer to handle the event
+ * @return api instance for method chaining
+ */
+ OpenFeatureAPI onProviderError(Consumer<EventDetails> handler);
+
+ /**
+ * Register an event handler for a specific provider event.
+ *
+ * @param event the provider event to listen for
+ * @param handler Consumer to handle the event
+ * @return api instance for method chaining
+ */
+ OpenFeatureAPI on(ProviderEvent event, Consumer<EventDetails> handler);
+
+ /**
+ * Remove an event handler for a specific provider event.
+ *
+ * @param event the provider event to stop listening for
+ * @param handler the handler to remove
+ * @return api instance for method chaining
+ */
+ OpenFeatureAPI removeHandler(ProviderEvent event, Consumer<EventDetails> handler);
+}
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureHooks.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureHooks.java
index 5888a65..a1fe84b 100644
--- c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureHooks.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureHooks.java
@@ -9,7 +9,7 @@ import java.util.List;
public interface OpenFeatureHooks {
/**
* Adds hooks for globally, used for all evaluations.
- * Hooks are run in the order they're added in the before stage.
+ * Hooks are run in the order they're added in the before stage.
* They are run in reverse order for all other stages.
*
* @param hooks The hooks to add.
@@ -27,4 +27,4 @@ public interface OpenFeatureHooks {
* Removes all hooks.
*/
void clearHooks();
-}
\ No newline at end of file
+}
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureLifecycle.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureLifecycle.java
new file mode 100644
index 0000000..6ba9733
--- /dev/null
+++ i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureLifecycle.java
@@ -0,0 +1,15 @@
+package dev.openfeature.api;
+
+/**
+ * Interface for OpenFeature API lifecycle management operations.
+ * Provides cleanup and shutdown capabilities for proper resource management.
+ */
+public interface OpenFeatureLifecycle {
+ /**
+ * Shut down and reset the current status of OpenFeature API.
+ * This call cleans up all active providers and attempts to shut down internal
+ * event handling mechanisms.
+ * Once shut down is complete, API is reset and ready to use again.
+ */
+ void shutdown();
+}
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureTransactionContext.java i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureTransactionContext.java
new file mode 100644
index 0000000..e5f94b1
--- /dev/null
+++ i/openfeature-api/src/main/java/dev/openfeature/api/OpenFeatureTransactionContext.java
@@ -0,0 +1,31 @@
+package dev.openfeature.api;
+
+/**
+ * Interface for transaction context management operations.
+ * Provides transaction-scoped context propagation and management,
+ * allowing for context to be passed across multiple operations
+ * within the same transaction or thread boundary.
+ */
+public interface OpenFeatureTransactionContext {
+ /**
+ * Return the transaction context propagator.
+ *
+ * @return the current transaction context propagator
+ */
+ TransactionContextPropagator getTransactionContextPropagator();
+
+ /**
+ * Sets the transaction context propagator.
+ *
+ * @param transactionContextPropagator the transaction context propagator to use
+ * @throws IllegalArgumentException if {@code transactionContextPropagator} is null
+ */
+ void setTransactionContextPropagator(TransactionContextPropagator transactionContextPropagator);
+
+ /**
+ * Sets the transaction context using the registered transaction context propagator.
+ *
+ * @param evaluationContext the evaluation context to set for the current transaction
+ */
+ void setTransactionContext(EvaluationContext evaluationContext);
+}
diff --git c/openfeature-sdk/src/main/java/dev/openfeature/sdk/Telemetry.java i/openfeature-api/src/main/java/dev/openfeature/api/Telemetry.java
similarity index 95%
rename from openfeature-sdk/src/main/java/dev/openfeature/sdk/Telemetry.java
rename to openfeature-api/src/main/java/dev/openfeature/api/Telemetry.java
index 3e1cf4b..31a4b4e 100644
--- c/openfeature-sdk/src/main/java/dev/openfeature/sdk/Telemetry.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/Telemetry.java
@@ -1,9 +1,5 @@
-package dev.openfeature.sdk;
+package dev.openfeature.api;
-import dev.openfeature.api.ErrorCode;
-import dev.openfeature.api.FlagEvaluationDetails;
-import dev.openfeature.api.HookContext;
-import dev.openfeature.api.Reason;
/**
* The Telemetry class provides constants and methods for creating OpenTelemetry compliant
* evaluation events.
diff --git c/openfeature-sdk/src/main/java/dev/openfeature/sdk/TransactionContextPropagator.java i/openfeature-api/src/main/java/dev/openfeature/api/TransactionContextPropagator.java
similarity index 92%
rename from openfeature-sdk/src/main/java/dev/openfeature/sdk/TransactionContextPropagator.java
rename to openfeature-api/src/main/java/dev/openfeature/api/TransactionContextPropagator.java
index 6507b64..7024124 100644
--- c/openfeature-sdk/src/main/java/dev/openfeature/sdk/TransactionContextPropagator.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/TransactionContextPropagator.java
@@ -1,6 +1,4 @@
-package dev.openfeature.sdk;
-
-import dev.openfeature.api.EvaluationContext;
+package dev.openfeature.api;
/**
* {@link TransactionContextPropagator} is responsible for persisting a transactional context
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/Value.java i/openfeature-api/src/main/java/dev/openfeature/api/Value.java
index 57d4efd..e7be432 100644
--- c/openfeature-api/src/main/java/dev/openfeature/api/Value.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/Value.java
@@ -306,8 +306,8 @@ public class Value implements Cloneable {
} else if (object instanceof Structure) {
return new Value((Structure) object);
} else if (object instanceof List) {
- return new Value(
- ((List<Object>) object).stream().map(o -> Value.objectToValue(o)).collect(Collectors.toList()));
+ return new Value(((List<Object>) object)
+ .stream().map(o -> Value.objectToValue(o)).collect(Collectors.toList()));
} else if (object instanceof Instant) {
return new Value((Instant) object);
} else if (object instanceof Map) {
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/NoOpClient.java i/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpClient.java
similarity index 77%
rename from openfeature-api/src/main/java/dev/openfeature/api/NoOpClient.java
rename to openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpClient.java
index d79d346..d4b2949 100644
--- c/openfeature-api/src/main/java/dev/openfeature/api/NoOpClient.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpClient.java
@@ -1,5 +1,17 @@
-package dev.openfeature.api;
+package dev.openfeature.api.internal.noop;
+import dev.openfeature.api.Client;
+import dev.openfeature.api.ClientMetadata;
+import dev.openfeature.api.EvaluationContext;
+import dev.openfeature.api.EventDetails;
+import dev.openfeature.api.FlagEvaluationDetails;
+import dev.openfeature.api.FlagEvaluationOptions;
+import dev.openfeature.api.Hook;
+import dev.openfeature.api.ProviderEvent;
+import dev.openfeature.api.ProviderState;
+import dev.openfeature.api.Reason;
+import dev.openfeature.api.TrackingEventDetails;
+import dev.openfeature.api.Value;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -7,8 +19,10 @@ import java.util.function.Consumer;
/**
* No-operation implementation of Client that provides safe defaults.
* All flag evaluations return default values and all operations are safe no-ops.
+ *
+ * <p><strong>This is an internal implementation class and should not be used directly by external users.</strong>
*/
-class NoOpClient implements Client {
+public class NoOpClient implements Client {
@Override
public ClientMetadata getMetadata() {
@@ -55,7 +69,8 @@ class NoOpClient implements Client {
}
@Override
- public FlagEvaluationDetails<Boolean> getBooleanDetails(String key, Boolean defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
+ public FlagEvaluationDetails<Boolean> getBooleanDetails(
+ String key, Boolean defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
return getBooleanDetails(key, defaultValue);
}
@@ -70,7 +85,8 @@ class NoOpClient implements Client {
}
@Override
- public Boolean getBooleanValue(String key, Boolean defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
+ public Boolean getBooleanValue(
+ String key, Boolean defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
return defaultValue;
}
@@ -89,7 +105,8 @@ class NoOpClient implements Client {
}
@Override
- public FlagEvaluationDetails<String> getStringDetails(String key, String defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
+ public FlagEvaluationDetails<String> getStringDetails(
+ String key, String defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
return getStringDetails(key, defaultValue);
}
@@ -104,7 +121,8 @@ class NoOpClient implements Client {
}
@Override
- public String getStringValue(String key, String defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
+ public String getStringValue(
+ String key, String defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
return defaultValue;
}
@@ -123,7 +141,8 @@ class NoOpClient implements Client {
}
@Override
- public FlagEvaluationDetails<Integer> getIntegerDetails(String key, Integer defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
+ public FlagEvaluationDetails<Integer> getIntegerDetails(
+ String key, Integer defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
return getIntegerDetails(key, defaultValue);
}
@@ -138,7 +157,8 @@ class NoOpClient implements Client {
}
@Override
- public Integer getIntegerValue(String key, Integer defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
+ public Integer getIntegerValue(
+ String key, Integer defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
return defaultValue;
}
@@ -157,7 +177,8 @@ class NoOpClient implements Client {
}
@Override
- public FlagEvaluationDetails<Double> getDoubleDetails(String key, Double defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
+ public FlagEvaluationDetails<Double> getDoubleDetails(
+ String key, Double defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
return getDoubleDetails(key, defaultValue);
}
@@ -172,7 +193,8 @@ class NoOpClient implements Client {
}
@Override
- public Double getDoubleValue(String key, Double defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
+ public Double getDoubleValue(
+ String key, Double defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
return defaultValue;
}
@@ -191,7 +213,8 @@ class NoOpClient implements Client {
}
@Override
- public FlagEvaluationDetails<Value> getObjectDetails(String key, Value defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
+ public FlagEvaluationDetails<Value> getObjectDetails(
+ String key, Value defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
return getObjectDetails(key, defaultValue);
}
@@ -259,4 +282,4 @@ class NoOpClient implements Client {
public Client removeHandler(ProviderEvent event, Consumer<EventDetails> handler) {
return this; // No-op - return self for chaining
}
-}
\ No newline at end of file
+}
diff --git c/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpOpenFeatureAPI.java i/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpOpenFeatureAPI.java
new file mode 100644
index 0000000..d3bdf95
--- /dev/null
+++ i/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpOpenFeatureAPI.java
@@ -0,0 +1,160 @@
+package dev.openfeature.api.internal.noop;
+
+import dev.openfeature.api.Client;
+import dev.openfeature.api.EvaluationContext;
+import dev.openfeature.api.EventDetails;
+import dev.openfeature.api.FeatureProvider;
+import dev.openfeature.api.Hook;
+import dev.openfeature.api.Metadata;
+import dev.openfeature.api.OpenFeatureAPI;
+import dev.openfeature.api.ProviderEvent;
+import dev.openfeature.api.TransactionContextPropagator;
+import dev.openfeature.api.exceptions.OpenFeatureError;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * No-operation implementation of OpenFeatureAPI that provides safe defaults.
+ * Used as a fallback when no actual implementation is available via ServiceLoader.
+ * All operations are safe no-ops that won't affect application functionality.
+ *
+ * <p>Package-private to prevent direct instantiation by external users.
+ */
+public class NoOpOpenFeatureAPI extends OpenFeatureAPI {
+
+ private static final NoOpClient NO_OP_CLIENT = new NoOpClient();
+ private static final NoOpProvider NO_OP_PROVIDER = new NoOpProvider();
+ private static final NoOpTransactionContextPropagator NO_OP_TRANSACTION_CONTEXT_PROPAGATOR =
+ new NoOpTransactionContextPropagator();
+
+ @Override
+ public Client getClient() {
+ return NO_OP_CLIENT;
+ }
+
+ @Override
+ public Client getClient(String domain) {
+ return NO_OP_CLIENT;
+ }
+
+ @Override
+ public Client getClient(String domain, String version) {
+ return NO_OP_CLIENT;
+ }
+
+ @Override
+ public void setProvider(FeatureProvider provider) {
+ // No-op - silently ignore
+ }
+
+ @Override
+ public void setProvider(String domain, FeatureProvider provider) {
+ // No-op - silently ignore
+ }
+
+ @Override
+ public void setProviderAndWait(FeatureProvider provider) throws OpenFeatureError {
+ // No-op - silently ignore
+ }
+
+ @Override
+ public void setProviderAndWait(String domain, FeatureProvider provider) throws OpenFeatureError {
+ // No-op - silently ignore
+ }
+
+ @Override
+ public FeatureProvider getProvider() {
+ return NO_OP_PROVIDER;
+ }
+
+ @Override
+ public FeatureProvider getProvider(String domain) {
+ return NO_OP_PROVIDER;
+ }
+
+ @Override
+ public Metadata getProviderMetadata() {
+ return () -> "No-op Provider";
+ }
+
+ @Override
+ public Metadata getProviderMetadata(String domain) {
+ return getProviderMetadata();
+ }
+
+ @Override
+ public void addHooks(Hook... hooks) {
+ // No-op - silently ignore
+ }
+
+ @Override
+ public List<Hook> getHooks() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public void clearHooks() {
+ // No-op - nothing to clear
+ }
+
+ @Override
+ public OpenFeatureAPI setEvaluationContext(EvaluationContext evaluationContext) {
+ return this; // No-op - return self for chaining
+ }
+
+ @Override
+ public EvaluationContext getEvaluationContext() {
+ return EvaluationContext.EMPTY;
+ }
+
+ @Override
+ public OpenFeatureAPI removeHandler(ProviderEvent event, Consumer<EventDetails> handler) {
+ return this;
+ }
+
+ @Override
+ public TransactionContextPropagator getTransactionContextPropagator() {
+ return NO_OP_TRANSACTION_CONTEXT_PROPAGATOR;
+ }
+
+ @Override
+ public void setTransactionContextPropagator(TransactionContextPropagator transactionContextPropagator) {
+ // No-op - silently ignore
+ }
+
+ @Override
+ public void setTransactionContext(EvaluationContext evaluationContext) {
+ // No-op - silently ignore
+ }
+
+ @Override
+ public void shutdown() {
+ // No-op - silently ignore
+ }
+
+ @Override
+ public OpenFeatureAPI onProviderReady(Consumer<EventDetails> handler) {
+ return this;
+ }
+
+ @Override
+ public OpenFeatureAPI onProviderConfigurationChanged(Consumer<EventDetails> handler) {
+ return this;
+ }
+
+ @Override
+ public OpenFeatureAPI onProviderStale(Consumer<EventDetails> handler) {
+ return this;
+ }
+
+ @Override
+ public OpenFeatureAPI onProviderError(Consumer<EventDetails> handler) {
+ return this;
+ }
+
+ @Override
+ public OpenFeatureAPI on(ProviderEvent event, Consumer<EventDetails> handler) {
+ return this;
+ }
+}
diff --git c/openfeature-sdk/src/main/java/dev/openfeature/sdk/NoOpProvider.java i/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpProvider.java
similarity index 94%
rename from openfeature-sdk/src/main/java/dev/openfeature/sdk/NoOpProvider.java
rename to openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpProvider.java
index d65041a..35c9b5d 100644
--- c/openfeature-sdk/src/main/java/dev/openfeature/sdk/NoOpProvider.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpProvider.java
@@ -1,4 +1,4 @@
-package dev.openfeature.sdk;
+package dev.openfeature.api.internal.noop;
import dev.openfeature.api.EvaluationContext;
import dev.openfeature.api.FeatureProvider;
@@ -11,6 +11,8 @@ import lombok.Getter;
/**
* A {@link FeatureProvider} that simply returns the default values passed to it.
+ *
+ * <p><strong>This is an internal implementation class and should not be used directly by external users.</strong>
*/
public class NoOpProvider implements FeatureProvider {
public static final String PASSED_IN_DEFAULT = "Passed in default";
diff --git c/openfeature-sdk/src/main/java/dev/openfeature/sdk/NoOpTransactionContextPropagator.java i/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpTransactionContextPropagator.java
similarity index 73%
rename from openfeature-sdk/src/main/java/dev/openfeature/sdk/NoOpTransactionContextPropagator.java
rename to openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpTransactionContextPropagator.java
index 0f1a71b..3dd64bf 100644
--- c/openfeature-sdk/src/main/java/dev/openfeature/sdk/NoOpTransactionContextPropagator.java
+++ i/openfeature-api/src/main/java/dev/openfeature/api/internal/noop/NoOpTransactionContextPropagator.java
@@ -1,9 +1,13 @@
-package dev.openfeature.sdk;
+package dev.openfeature.api.internal.noop;
import dev.openfeature.api.EvaluationContext;
import dev.openfeature.api.ImmutableContext;
+import dev.openfeature.api.TransactionContextPropagator;
+
/**
* A {@link TransactionContextPropagator} that simply returns empty context.
+ *
+ * <p><strong>This is an internal implementation class and should not be used directly by external users.</strong>
*/
public class NoOpTransactionContextPropagator implements TransactionContextPropagator {
diff --git c/openfeature-api/src/main/java/module-info.java i/openfeature-api/src/main/java/module-info.java
new file mode 100644
index 0000000..95c41e5
--- /dev/null
+++ i/openfeature-api/src/main/java/module-info.java
@@ -0,0 +1,14 @@
+module dev.openfeature.api {
+ requires static lombok;
+ requires org.slf4j;
+ requires com.github.spotbugs.annotations;
+
+ exports dev.openfeature.api;
+ exports dev.openfeature.api.exceptions;
+ exports dev.openfeature.api.internal.noop;
+
+ uses dev.openfeature.api.OpenFeatureAPIProvider;
+
+ opens dev.openfeature.api to lombok;
+ opens dev.openfeature.api.exceptions to lombok;
+}
diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/FlagEvaluationDetailsTest.java i/openfeature-api/src/test/java/dev/openfeature/api/FlagEvaluationDetailsTest.java
similarity index 98%
rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/FlagEvaluationDetailsTest.java
rename to openfeature-api/src/test/java/dev/openfeature/api/FlagEvaluationDetailsTest.java
index 345a7ef..3539636 100644
--- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/FlagEvaluationDetailsTest.java
+++ i/openfeature-api/src/test/java/dev/openfeature/api/FlagEvaluationDetailsTest.java
@@ -1,4 +1,4 @@
-package dev.openfeature.sdk;
+package dev.openfeature.api;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/FlagMetadataTest.java i/openfeature-api/src/test/java/dev/openfeature/api/FlagMetadataTest.java
similarity index 99%
rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/FlagMetadataTest.java
rename to openfeature-api/src/test/java/dev/openfeature/api/FlagMetadataTest.java
index 2291266..b4c637b 100644
--- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/FlagMetadataTest.java
+++ i/openfeature-api/src/test/java/dev/openfeature/api/FlagMetadataTest.java
@@ -1,4 +1,4 @@
-package dev.openfeature.sdk;
+package dev.openfeature.api;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse;
diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableContextTest.java i/openfeature-api/src/test/java/dev/openfeature/api/ImmutableContextTest.java
similarity index 98%
rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableContextTest.java
rename to openfeature-api/src/test/java/dev/openfeature/api/ImmutableContextTest.java
index 2b39be7..8ae55d2 100644
--- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableContextTest.java
+++ i/openfeature-api/src/test/java/dev/openfeature/api/ImmutableContextTest.java
@@ -1,6 +1,6 @@
-package dev.openfeature.sdk;
+package dev.openfeature.api;
-import static dev.openfeature.sdk.EvaluationContext.TARGETING_KEY;
+import static dev.openfeature.api.EvaluationContext.TARGETING_KEY;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableMetadataTest.java i/openfeature-api/src/test/java/dev/openfeature/api/ImmutableMetadataTest.java
similarity index 97%
rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableMetadataTest.java
rename to openfeature-api/src/test/java/dev/openfeature/api/ImmutableMetadataTest.java
index 5f176f1..db33f08 100644
--- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableMetadataTest.java
+++ i/openfeature-api/src/test/java/dev/openfeature/api/ImmutableMetadataTest.java
@@ -1,4 +1,4 @@
-package dev.openfeature.sdk;
+package dev.openfeature.api;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableStructureTest.java i/openfeature-api/src/test/java/dev/openfeature/api/ImmutableStructureTest.java
similarity index 99%
rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableStructureTest.java
rename to openfeature-api/src/test/java/dev/openfeature/api/ImmutableStructureTest.java
index 6a0eed5..63f2702 100644
--- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ImmutableStructureTest.java
+++ i/openfeature-api/src/test/java/dev/openfeature/api/ImmutableStructureTest.java
@@ -1,4 +1,4 @@
-package dev.openfeature.sdk;
+package dev.openfeature.api;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/MutableContextTest.java i/openfeature-api/src/test/java/dev/openfeature/api/MutableContextTest.java
similarity index 98%
rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/MutableContextTest.java
rename to openfeature-api/src/test/java/dev/openfeature/api/MutableContextTest.java
index 6c471d0..a9a8714 100644
--- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/MutableContextTest.java
+++ i/openfeature-api/src/test/java/dev/openfeature/api/MutableContextTest.java
@@ -1,6 +1,6 @@
-package dev.openfeature.sdk;
+package dev.openfeature.api;
-import static dev.openfeature.sdk.EvaluationContext.TARGETING_KEY;
+import static dev.openfeature.api.EvaluationContext.TARGETING_KEY;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/MutableStructureTest.java i/openfeature-api/src/test/java/dev/openfeature/api/MutableStructureTest.java
similarity index 98%
rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/MutableStructureTest.java
rename to openfeature-api/src/test/java/dev/openfeature/api/MutableStructureTest.java
index ebd11af..91f473c 100644
--- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/MutableStructureTest.java
+++ i/openfeature-api/src/test/java/dev/openfeature/api/MutableStructureTest.java
@@ -1,4 +1,4 @@
-package dev.openfeature.sdk;
+package dev.openfeature.api;
import static org.junit.jupiter.api.Assertions.*;
diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ProviderEvaluationTest.java i/openfeature-api/src/test/java/dev/openfeature/api/ProviderEvaluationTest.java
similarity index 98%
rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/ProviderEvaluationTest.java
rename to openfeature-api/src/test/java/dev/openfeature/api/ProviderEvaluationTest.java
index 2476243..2040c63 100644
--- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ProviderEvaluationTest.java
+++ i/openfeature-api/src/test/java/dev/openfeature/api/ProviderEvaluationTest.java
@@ -1,4 +1,4 @@
-package dev.openfeature.sdk;
+package dev.openfeature.api;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/StructureTest.java i/openfeature-api/src/test/java/dev/openfeature/api/StructureTest.java
similarity index 98%
rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/StructureTest.java
rename to openfeature-api/src/test/java/dev/openfeature/api/StructureTest.java
index 2a2406a..3c15e01 100644
--- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/StructureTest.java
+++ i/openfeature-api/src/test/java/dev/openfeature/api/StructureTest.java
@@ -1,6 +1,6 @@
-package dev.openfeature.sdk;
+package dev.openfeature.api;
-import static dev.openfeature.sdk.Structure.mapToStructure;
+import static dev.openfeature.api.Structure.mapToStructure;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertNull;
diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ValueTest.java i/openfeature-api/src/test/java/dev/openfeature/api/ValueTest.java
similarity index 99%
rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/ValueTest.java
rename to openfeature-api/src/test/java/dev/openfeature/api/ValueTest.java
index 697edb7..788c3f6 100644
--- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/ValueTest.java
+++ i/openfeature-api/src/test/java/dev/openfeature/api/ValueTest.java
@@ -1,4 +1,4 @@
-package dev.openfeature.sdk;
+package dev.openfeature.api;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
diff --git c/openfeature-sdk/src/test/java/dev/openfeature/sdk/exceptions/ExceptionUtilsTest.java i/openfeature-api/src/test/java/dev/openfeature/api/exceptions/ExceptionUtilsTest.java
similarity index 96%
rename from openfeature-sdk/src/test/java/dev/openfeature/sdk/exceptions/ExceptionUtilsTest.java
rename to openfeature-api/src/test/java/dev/openfeature/api/exceptions/ExceptionUtilsTest.java
index 0a9a522..0021571 100644
--- c/openfeature-sdk/src/test/java/dev/openfeature/sdk/exceptions/ExceptionUtilsTest.java
+++ i/openfeature-api/src/test/java/dev/openfeature/api/exceptions/ExceptionUtilsTest.java
@@ -1,9 +1,9 @@
-package dev.openfeature.sdk.exceptions;
+package dev.openfeature.api.exceptions;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
-import dev.openfeature.sdk.ErrorCode;
+import dev.openfeature.api.ErrorCode;
import java.util.stream.Stream;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.extension.ExtensionContext;
diff --git c/openfeature-sdk/pom.xml i/openfeature-sdk/pom.xml
index 6e4f367..3fa10b5 100644
--- c/openfeature-sdk/pom.xml
+++ i/openfeature-sdk/pom.xml
@@ -43,9 +43,88 @@
<scope>provided</scope>
</dependency>
- <!-- SLF4J already included from API module -->
+ <!-- SLF4J dependency needed for Lombok @Slf4j -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
- <!-- Test dependencies will be added later -->
+ <!-- Test dependencies (copied from parent) -->
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <version>5.13.4</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.platform</groupId>
+ <artifactId>junit-platform-suite</artifactId>
+ <version>1.13.4</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>${org.mockito.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>3.27.3</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.awaitility</groupId>
+ <artifactId>awaitility</artifactId>
+ <version>4.3.0</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>io.cucumber</groupId>
+ <artifactId>cucumber-java</artifactId>
+ <version>7.27.0</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>io.cucumber</groupId>
+ <artifactId>cucumber-junit-platform-engine</artifactId>
+ <version>7.27.0</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.simplify4u</groupId>
+ <artifactId>slf4j2-mock</artifactId>
+ <version>2.4.0</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>33.4.8-jre</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.tngtech.archunit</groupId>
+ <artifactId>archunit-junit5</artifactId>
+ <version>1.4.1</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openjdk.jmh</groupId>
+ <artifactId>jmh-core</artifactId>
+ <ver…1 parent c6a039b commit 1231328
94 files changed
Lines changed: 1848 additions & 486 deletions
File tree
- openfeature-api
- src
- main/java
- dev/openfeature/api
- internal/noop
- test/java/dev/openfeature/api
- exceptions
- openfeature-sdk
- src
- main/java
- dev/openfeature/sdk
- test/java/dev/openfeature/sdk
- benchmark
- e2e
- steps
- fixtures
- hooks/logging
- providers/memory
- testutils
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
| 10 | + | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
| 13 | + | |
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
18 | | - | |
| 18 | + | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
| 21 | + | |
22 | 22 | | |
23 | 23 | | |
24 | | - | |
| 24 | + | |
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
47 | | - | |
| 47 | + | |
48 | 48 | | |
49 | 49 | | |
50 | 50 | | |
51 | 51 | | |
52 | | - | |
| 52 | + | |
53 | 53 | | |
54 | 54 | | |
55 | | - | |
| 55 | + | |
56 | 56 | | |
57 | 57 | | |
58 | | - | |
| 58 | + | |
59 | 59 | | |
60 | 60 | | |
61 | | - | |
| 61 | + | |
62 | 62 | | |
63 | 63 | | |
64 | | - | |
| 64 | + | |
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
| |||
80 | 80 | | |
81 | 81 | | |
82 | 82 | | |
83 | | - | |
| 83 | + | |
84 | 84 | | |
85 | | - | |
| 85 | + | |
86 | 86 | | |
87 | 87 | | |
88 | 88 | | |
89 | 89 | | |
90 | | - | |
| 90 | + | |
91 | 91 | | |
92 | 92 | | |
93 | | - | |
| 93 | + | |
94 | 94 | | |
95 | 95 | | |
96 | | - | |
| 96 | + | |
97 | 97 | | |
98 | 98 | | |
99 | | - | |
| 99 | + | |
100 | 100 | | |
101 | 101 | | |
102 | | - | |
| 102 | + | |
103 | 103 | | |
104 | | - | |
105 | | - | |
| 104 | + | |
| 105 | + | |
106 | 106 | | |
107 | 107 | | |
108 | 108 | | |
| |||
150 | 150 | | |
151 | 151 | | |
152 | 152 | | |
153 | | - | |
| 153 | + | |
154 | 154 | | |
155 | 155 | | |
156 | 156 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
45 | | - | |
| 45 | + | |
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
| |||
77 | 77 | | |
78 | 78 | | |
79 | 79 | | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
80 | 112 | | |
81 | 113 | | |
82 | 114 | | |
83 | | - | |
| 115 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | | - | |
| 15 | + | |
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| |||
Lines changed: 20 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
14 | 15 | | |
| 16 | + | |
| 17 | + | |
15 | 18 | | |
16 | 19 | | |
17 | | - | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
18 | 29 | | |
19 | 30 | | |
20 | 31 | | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
21 | 40 | | |
22 | 41 | | |
23 | 42 | | |
| |||
Lines changed: 64 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | | - | |
| 9 | + | |
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| |||
Lines changed: 0 additions & 88 deletions
This file was deleted.
0 commit comments