hexastack-flags 0.0.0__py3-none-any.whl

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.

Potentially problematic release.


This version of hexastack-flags might be problematic. Click here for more details.

@@ -0,0 +1,12 @@
1
+ from hexastack_flags import adapters, domain, infra, ports
2
+ from hexastack_flags.adapters.openfeature import OpenFeatureFlagAdapter
3
+ from hexastack_flags.infra.bootstrap import FeatureFlagBootstrapper
4
+
5
+ __all__ = [
6
+ "adapters",
7
+ "domain",
8
+ "FeatureFlagBootstrapper",
9
+ "infra",
10
+ "OpenFeatureFlagAdapter",
11
+ "ports",
12
+ ]
@@ -0,0 +1,5 @@
1
+ from hexastack_flags.adapters.openfeature import OpenFeatureFlagAdapter
2
+
3
+ __all__ = [
4
+ "OpenFeatureFlagAdapter",
5
+ ]
@@ -0,0 +1,211 @@
1
+ """CNCF OpenFeature flag adapter implementation.
2
+
3
+ Notes/Architectural Intent:
4
+ Adapts the OpenFeature Python SDK (openfeature.api) to Hexastack's FeatureFlagPort.
5
+ Translates ambient UserContext / EvaluationContext seamlessly to OpenFeature
6
+ EvaluationContext and evaluates boolean, string, int, float, and JSON flags.
7
+ """
8
+
9
+ from collections.abc import Mapping
10
+ from typing import Any
11
+
12
+ import openfeature.api
13
+ from openfeature.client import OpenFeatureClient
14
+ from openfeature.evaluation_context import EvaluationContext as OfEvaluationContext
15
+
16
+ from hexastack_core.domain.feature_flags import (
17
+ EvaluationContext,
18
+ FlagEvaluationDetails,
19
+ FlagEvaluationReason,
20
+ )
21
+ from hexastack_core.ports.feature_flags import FeatureFlagPort
22
+
23
+ __all__ = [
24
+ "OpenFeatureFlagAdapter",
25
+ ]
26
+
27
+
28
+ class OpenFeatureFlagAdapter(FeatureFlagPort):
29
+ """FeatureFlagPort implementation delegating evaluations to OpenFeature SDK.
30
+
31
+ Notes/Architectural Intent:
32
+ Wraps an OpenFeature Client instance, connecting Hexastack application
33
+ layers (CQRS, FastAPI, CLI, GraphQL) to any configured OpenFeature provider.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ client: OpenFeatureClient | None = None,
39
+ client_name: str | None = None,
40
+ ) -> None:
41
+ self._client = client or openfeature.api.get_client(client_name)
42
+
43
+ def get_all_flags(self) -> dict[str, Any]:
44
+ """Introspect active flags from the underlying OpenFeature provider if supported.
45
+
46
+ Returns:
47
+ Dictionary mapping flag keys to their configured status/values.
48
+ """
49
+ flags: dict[str, Any] = {}
50
+ try:
51
+ provider: Any = openfeature.api.provider_registry.get_provider(None)
52
+ provider_flags = getattr(provider, "_flags", None)
53
+ if isinstance(provider_flags, dict):
54
+ for k, v in provider_flags.items():
55
+ # If InMemoryFlag object, resolve current variant/value
56
+ if hasattr(v, "variants") and hasattr(v, "default_variant"):
57
+ flags[str(k)] = v.variants.get(
58
+ v.default_variant, v.default_variant
59
+ )
60
+ elif hasattr(v, "state"):
61
+ flags[str(k)] = str(v.state)
62
+ else:
63
+ flags[str(k)] = v
64
+ except Exception:
65
+ pass
66
+ return flags
67
+
68
+ def get_boolean_details(
69
+ self,
70
+ flag_key: str,
71
+ default: bool = False,
72
+ context: EvaluationContext | None = None,
73
+ ) -> FlagEvaluationDetails[bool]:
74
+ """Evaluate a boolean flag returning comprehensive evaluation metadata."""
75
+ of_ctx = _to_openfeature_context(
76
+ context or EvaluationContext.from_current_context()
77
+ )
78
+ details = self._client.get_boolean_details(
79
+ flag_key=flag_key,
80
+ default_value=default,
81
+ evaluation_context=of_ctx,
82
+ )
83
+ reason: FlagEvaluationReason = FlagEvaluationReason.UNKNOWN
84
+ if details.reason:
85
+ try:
86
+ reason = FlagEvaluationReason(str(details.reason).upper())
87
+ except ValueError:
88
+ reason = FlagEvaluationReason.UNKNOWN
89
+
90
+ return FlagEvaluationDetails(
91
+ flag_key=details.flag_key,
92
+ value=details.value,
93
+ reason=reason,
94
+ variant=details.variant,
95
+ error_code=str(details.error_code) if details.error_code else None,
96
+ )
97
+
98
+ def get_boolean_value(
99
+ self,
100
+ flag_key: str,
101
+ default: bool = False,
102
+ context: EvaluationContext | None = None,
103
+ ) -> bool:
104
+ """Evaluate a boolean feature flag."""
105
+ of_ctx = _to_openfeature_context(
106
+ context or EvaluationContext.from_current_context()
107
+ )
108
+ return self._client.get_boolean_value(
109
+ flag_key=flag_key,
110
+ default_value=default,
111
+ evaluation_context=of_ctx,
112
+ )
113
+
114
+ def get_float_value(
115
+ self,
116
+ flag_key: str,
117
+ default: float = 0.0,
118
+ context: EvaluationContext | None = None,
119
+ ) -> float:
120
+ """Evaluate a floating-point feature flag."""
121
+ of_ctx = _to_openfeature_context(
122
+ context or EvaluationContext.from_current_context()
123
+ )
124
+ return self._client.get_float_value(
125
+ flag_key=flag_key,
126
+ default_value=default,
127
+ evaluation_context=of_ctx,
128
+ )
129
+
130
+ def get_integer_value(
131
+ self,
132
+ flag_key: str,
133
+ default: int = 0,
134
+ context: EvaluationContext | None = None,
135
+ ) -> int:
136
+ """Evaluate an integer feature flag."""
137
+ of_ctx = _to_openfeature_context(
138
+ context or EvaluationContext.from_current_context()
139
+ )
140
+ return self._client.get_integer_value(
141
+ flag_key=flag_key,
142
+ default_value=default,
143
+ evaluation_context=of_ctx,
144
+ )
145
+
146
+ def get_object_value(
147
+ self,
148
+ flag_key: str,
149
+ default: Mapping[str, Any] | None = None,
150
+ context: EvaluationContext | None = None,
151
+ ) -> Mapping[str, Any]:
152
+ """Evaluate a structured JSON object feature flag."""
153
+ of_ctx = _to_openfeature_context(
154
+ context or EvaluationContext.from_current_context()
155
+ )
156
+ default_val = dict(default) if default is not None else {}
157
+ res = self._client.get_object_value(
158
+ flag_key=flag_key,
159
+ default_value=default_val,
160
+ evaluation_context=of_ctx,
161
+ )
162
+ return res if isinstance(res, Mapping) else default_val
163
+
164
+ def get_string_value(
165
+ self,
166
+ flag_key: str,
167
+ default: str = "",
168
+ context: EvaluationContext | None = None,
169
+ ) -> str:
170
+ """Evaluate a string/variant feature flag."""
171
+ of_ctx = _to_openfeature_context(
172
+ context or EvaluationContext.from_current_context()
173
+ )
174
+ return self._client.get_string_value(
175
+ flag_key=flag_key,
176
+ default_value=default,
177
+ evaluation_context=of_ctx,
178
+ )
179
+
180
+ def is_enabled(
181
+ self,
182
+ flag_key: str,
183
+ default: bool = False,
184
+ context: EvaluationContext | None = None,
185
+ ) -> bool:
186
+ """Evaluate whether a boolean feature flag is enabled."""
187
+ return self.get_boolean_value(
188
+ flag_key=flag_key, default=default, context=context
189
+ )
190
+
191
+
192
+ def _to_openfeature_context(
193
+ context: EvaluationContext | None,
194
+ ) -> OfEvaluationContext | None:
195
+ """Convert Hexastack EvaluationContext to OpenFeature EvaluationContext."""
196
+ if context is None:
197
+ return None
198
+
199
+ targeting_key = context.targeting_key or context.user_id or ""
200
+ attributes: dict[str, Any] = dict(context.attributes)
201
+ if context.user_id:
202
+ attributes["user_id"] = context.user_id
203
+ if context.tenant_id:
204
+ attributes["tenant_id"] = context.tenant_id
205
+ if context.roles:
206
+ attributes["roles"] = list(context.roles)
207
+
208
+ return OfEvaluationContext(
209
+ targeting_key=targeting_key,
210
+ attributes=attributes,
211
+ )
@@ -0,0 +1,5 @@
1
+ from hexastack_flags.adapters.providers.factory import initialize_openfeature_provider
2
+
3
+ __all__ = [
4
+ "initialize_openfeature_provider",
5
+ ]
@@ -0,0 +1,88 @@
1
+ """Factory for initializing OpenFeature provider backends.
2
+
3
+ Notes/Architectural Intent:
4
+ Decouples application setup from concrete OpenFeature provider implementations
5
+ (Flagd, In-Memory, etc.), dynamically importing optional dependencies on demand.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any
11
+
12
+ import openfeature.api
13
+ from openfeature.provider.in_memory_provider import InMemoryFlag, InMemoryProvider
14
+
15
+ from hexastack_core.domain.exceptions import MissingDependencyError
16
+ from hexastack_flags.domain.models import (
17
+ FeatureFlagProviderType,
18
+ FlagProviderOptions,
19
+ )
20
+
21
+
22
+ def initialize_openfeature_provider(
23
+ provider_type: FeatureFlagProviderType | str = FeatureFlagProviderType.IN_MEMORY,
24
+ options: FlagProviderOptions | None = None,
25
+ in_memory_flags: dict[str, Any] | None = None,
26
+ ) -> None:
27
+ """Initialize and register an OpenFeature provider backend globally.
28
+
29
+ Args:
30
+ provider_type: Target backend ("flagd", "in_memory", "env", etc.).
31
+ options: Configuration options (host, port, cache, etc.).
32
+ in_memory_flags: Initial flags dict when using in_memory provider.
33
+
34
+ Raises:
35
+ MissingDependencyError: If a provider extra (e.g. openfeature-provider-flagd) is missing.
36
+ """
37
+ opts = options or FlagProviderOptions()
38
+ p_type = str(provider_type).lower()
39
+
40
+ if p_type == FeatureFlagProviderType.FLAGD:
41
+ try:
42
+ import importlib
43
+
44
+ flagd_mod = importlib.import_module("openfeature.contrib.provider.flagd")
45
+ config_mod = importlib.import_module(
46
+ "openfeature.contrib.provider.flagd.config"
47
+ )
48
+ flagd_cls = flagd_mod.FlagdProvider
49
+ cache_type_cls = config_mod.CacheType
50
+
51
+ cache_val = cache_type_cls.LRU if opts.cache else cache_type_cls.DISABLED
52
+
53
+ provider = flagd_cls(
54
+ host=opts.host,
55
+ port=opts.port,
56
+ cache=cache_val,
57
+ timeout=opts.timeout_ms,
58
+ )
59
+ openfeature.api.set_provider(provider)
60
+ except (ImportError, AttributeError) as e:
61
+ raise MissingDependencyError(
62
+ "openfeature-provider-flagd is required for Flagd provider. "
63
+ "Install with 'pip install hexastack-flags[flagd]'."
64
+ ) from e
65
+
66
+ elif p_type == FeatureFlagProviderType.IN_MEMORY:
67
+ flags_dict: dict[str, InMemoryFlag[Any]] = {}
68
+ if in_memory_flags:
69
+ for k, v in in_memory_flags.items():
70
+ if isinstance(v, bool):
71
+ flags_dict[k] = InMemoryFlag(
72
+ state=InMemoryFlag.State.ENABLED,
73
+ default_variant="on" if v else "off",
74
+ variants={"on": True, "off": False},
75
+ )
76
+ else:
77
+ flags_dict[k] = InMemoryFlag(
78
+ state=InMemoryFlag.State.ENABLED,
79
+ default_variant="default",
80
+ variants={"default": v},
81
+ )
82
+ provider = InMemoryProvider(flags_dict)
83
+ openfeature.api.set_provider(provider)
84
+
85
+
86
+ __all__ = [
87
+ "initialize_openfeature_provider",
88
+ ]
@@ -0,0 +1,9 @@
1
+ from hexastack_flags.domain.models import (
2
+ FeatureFlagProviderType,
3
+ FlagProviderOptions,
4
+ )
5
+
6
+ __all__ = [
7
+ "FeatureFlagProviderType",
8
+ "FlagProviderOptions",
9
+ ]
@@ -0,0 +1,35 @@
1
+ """Domain models and enumerations for OpenFeature integration.
2
+
3
+ Notes/Architectural Intent:
4
+ Encapsulates OpenFeature provider types, provider options, and evaluation
5
+ metadata conforming to the CNCF OpenFeature specification.
6
+ """
7
+
8
+ from enum import StrEnum
9
+ from typing import Any
10
+
11
+ from pydantic import BaseModel, Field
12
+
13
+ __all__ = [
14
+ "FeatureFlagProviderType",
15
+ "FlagProviderOptions",
16
+ ]
17
+
18
+
19
+ class FeatureFlagProviderType(StrEnum):
20
+ """Supported CNCF OpenFeature provider backends."""
21
+
22
+ FLAGD = "flagd"
23
+ IN_MEMORY = "in_memory"
24
+ ENV = "env"
25
+ CUSTOM = "custom"
26
+
27
+
28
+ class FlagProviderOptions(BaseModel):
29
+ """Configuration options for OpenFeature provider initialization."""
30
+
31
+ host: str = "localhost"
32
+ port: int = 8013
33
+ cache: bool = True
34
+ timeout_ms: int = 5000
35
+ extra: dict[str, Any] = Field(default_factory=dict)
@@ -0,0 +1,8 @@
1
+ from hexastack_flags.infra.bootstrap import FeatureFlagBootstrapper
2
+ from hexastack_flags.infra.config import HexastackFlagsConfig, register_flags_config
3
+
4
+ __all__ = [
5
+ "FeatureFlagBootstrapper",
6
+ "HexastackFlagsConfig",
7
+ "register_flags_config",
8
+ ]
@@ -0,0 +1,52 @@
1
+ """Bootstrapper extension for hexastack-flags.
2
+
3
+ Notes/Architectural Intent:
4
+ Configures the OpenFeature provider and registers OpenFeatureFlagAdapter
5
+ into the rodi.Container as the singleton FeatureFlagPort at order=14.
6
+ """
7
+
8
+ from hexastack_core.infra.bootstrap import BootstrapContext
9
+ from hexastack_core.infra.registries.config import ConfigRegistry
10
+ from hexastack_core.ports.bootstrap import BootstrapperPort
11
+ from hexastack_core.ports.feature_flags import FeatureFlagPort
12
+ from hexastack_flags.adapters.openfeature import OpenFeatureFlagAdapter
13
+ from hexastack_flags.adapters.providers.factory import initialize_openfeature_provider
14
+ from hexastack_flags.domain.models import FlagProviderOptions
15
+ from hexastack_flags.infra.config import HexastackFlagsConfig, register_flags_config
16
+
17
+ __all__ = [
18
+ "FeatureFlagBootstrapper",
19
+ ]
20
+
21
+
22
+ class FeatureFlagBootstrapper(BootstrapperPort):
23
+ """Bootstrapper that configures CNCF OpenFeature and binds OpenFeatureFlagAdapter."""
24
+
25
+ name: str = "feature_flags"
26
+ order: int = 14 # Runs before CQRS (20), FastAPI (30), etc.
27
+
28
+ def configure(self, context: BootstrapContext) -> None:
29
+ """Initialize OpenFeature provider and register OpenFeatureFlagAdapter in DI."""
30
+ config = context.get_config("flags", HexastackFlagsConfig)
31
+
32
+ options = FlagProviderOptions(
33
+ host=config.host,
34
+ port=config.port,
35
+ cache=config.cache,
36
+ timeout_ms=config.timeout_ms,
37
+ extra=config.options,
38
+ )
39
+
40
+ initialize_openfeature_provider(
41
+ provider_type=config.provider,
42
+ options=options,
43
+ in_memory_flags=config.flags,
44
+ )
45
+
46
+ adapter = OpenFeatureFlagAdapter()
47
+ context.container.add_instance(adapter, declared_class=FeatureFlagPort)
48
+ context.flags = adapter
49
+
50
+ def register_config(self, registry: ConfigRegistry) -> None:
51
+ """Register HexastackFlagsConfig under [hexastack.flags]."""
52
+ register_flags_config(registry)
@@ -0,0 +1,49 @@
1
+ """Configuration schemas for hexastack-flags.
2
+
3
+ Notes/Architectural Intent:
4
+ Defines Pydantic configuration schemas loaded from [hexastack.flags].
5
+ """
6
+
7
+ from typing import Any
8
+
9
+ from pydantic import BaseModel, Field
10
+
11
+ from hexastack_core.infra.decorators import config_section
12
+ from hexastack_core.infra.registries.config import ConfigRegistry
13
+ from hexastack_flags.domain.models import FeatureFlagProviderType
14
+
15
+ __all__ = [
16
+ "HexastackFlagsConfig",
17
+ "register_flags_config",
18
+ ]
19
+
20
+
21
+ @config_section("flags")
22
+ class HexastackFlagsConfig(BaseModel):
23
+ """Configuration options under [hexastack.flags]."""
24
+
25
+ provider: str = Field(
26
+ default=FeatureFlagProviderType.IN_MEMORY.value,
27
+ description="Feature flag provider backend: 'flagd', 'in_memory', 'env'",
28
+ )
29
+ host: str = Field(default="localhost", description="Flagd server hostname")
30
+ port: int = Field(default=8013, description="Flagd server port")
31
+ cache: bool = Field(
32
+ default=True, description="Enable local in-memory evaluation cache"
33
+ )
34
+ timeout_ms: int = Field(
35
+ default=5000, description="Evaluation timeout in milliseconds"
36
+ )
37
+ flags: dict[str, Any] = Field(
38
+ default_factory=dict,
39
+ description="Static in-memory feature flags mapping (key -> bool/value)",
40
+ )
41
+ options: dict[str, Any] = Field(
42
+ default_factory=dict,
43
+ description="Additional provider-specific configuration options",
44
+ )
45
+
46
+
47
+ def register_flags_config(registry: ConfigRegistry) -> None:
48
+ """Register the flags configuration schema with the ConfigRegistry."""
49
+ registry.register_config_section("flags", HexastackFlagsConfig)
@@ -0,0 +1,11 @@
1
+ """Ports module for hexastack_flags.
2
+
3
+ Notes/Architectural Intent:
4
+ Re-exports FeatureFlagPort from hexastack_core for hexagonal boundary consistency.
5
+ """
6
+
7
+ from hexastack_core.ports.feature_flags import FeatureFlagPort
8
+
9
+ __all__ = [
10
+ "FeatureFlagPort",
11
+ ]
@@ -0,0 +1,96 @@
1
+ Metadata-Version: 2.3
2
+ Name: hexastack-flags
3
+ Version: 0.0.0
4
+ Summary: CNCF OpenFeature provider adapters and enterprise feature flagging for Hexastack
5
+ Author: Richard West
6
+ Author-email: Richard West <dopplereffect.us@gmail.com>
7
+ Requires-Dist: hexastack-core
8
+ Requires-Dist: openfeature-sdk>=0.7.1
9
+ Requires-Dist: pydantic>=2.10.0
10
+ Requires-Dist: openfeature-provider-flagd>=0.2.0 ; extra == 'flagd'
11
+ Requires-Python: >=3.13
12
+ Provides-Extra: flagd
13
+ Description-Content-Type: text/markdown
14
+
15
+ # hexastack-flags
16
+
17
+ **CNCF OpenFeature provider adapters and enterprise feature flagging for Hexastack.**
18
+
19
+ Part of the [Hexastack Framework](https://github.com/TheTrueSCU/hexastack).
20
+
21
+ ---
22
+
23
+ ## 1. Architectural Overview
24
+
25
+ `hexastack-flags` provides the production-grade adapter connecting Hexastack's [`FeatureFlagPort`](file:///home/rjdw/Projects/hexastack/packages/hexastack_core/src/hexastack_core/ports/feature_flags.py) to the **CNCF OpenFeature Python SDK**.
26
+
27
+ It decouples your domain, CQRS handlers, REST routes, Typer commands, and GraphQL resolvers from proprietary feature flagging APIs, allowing seamless integration with **Flagd**, **Unleash**, **Flipt**, **GO Feature Flag**, or local in-memory providers.
28
+
29
+ ```mermaid
30
+ graph TD
31
+ APP["Driving Adapters (FastAPI / CLI / GraphQL)"]
32
+ CQRS["CQRS Middleware Pipeline"]
33
+ PORT["FeatureFlagPort (hexastack-core)"]
34
+ ADAPTER["OpenFeatureFlagAdapter (hexastack-flags)"]
35
+ OF_SDK["CNCF OpenFeature SDK"]
36
+
37
+ subgraph Providers ["OpenFeature Providers"]
38
+ FLAGD["FlagdProvider (gRPC / Kubernetes)"]
39
+ UNLEASH["UnleashProvider"]
40
+ MEM["InMemoryProvider"]
41
+ ENV["EnvProvider"]
42
+ end
43
+
44
+ APP --> PORT
45
+ CQRS --> PORT
46
+ PORT -.-> ADAPTER
47
+ ADAPTER --> OF_SDK
48
+ OF_SDK --> FLAGD
49
+ OF_SDK --> UNLEASH
50
+ OF_SDK --> MEM
51
+ OF_SDK --> ENV
52
+ ```
53
+
54
+ ---
55
+
56
+ ## 2. Quickstart
57
+
58
+ ### Installation
59
+
60
+ ```bash
61
+ # Core OpenFeature adapter with In-Memory support
62
+ pip install hexastack-flags
63
+
64
+ # With Flagd provider
65
+ pip install "hexastack-flags[flagd]"
66
+ ```
67
+
68
+ ### Configuration (`hexastack.toml` or `pyproject.toml`)
69
+
70
+ ```toml
71
+ [hexastack.flags]
72
+ provider = "flagd" # "in_memory", "flagd", "env"
73
+ host = "localhost"
74
+ port = 8013
75
+ cache = true
76
+ timeout_ms = 5000
77
+ ```
78
+
79
+ ---
80
+
81
+ ## 3. Evaluation Examples
82
+
83
+ ```python
84
+ from hexastack_core.ports.feature_flags import FeatureFlagPort
85
+ from hexastack_core.domain.feature_flags import EvaluationContext
86
+
87
+ flags = container.resolve(FeatureFlagPort)
88
+
89
+ # 1. Simple boolean toggle
90
+ if flags.is_enabled("features.checkout.v2", default=False):
91
+ ...
92
+
93
+ # 2. Contextual evaluation (multi-tenant / user targeting)
94
+ ctx = EvaluationContext(user_id="usr_123", tenant_id="tenant-alpha")
95
+ theme = flags.get_string_value("features.ui.theme", default="light", context=ctx)
96
+ ```
@@ -0,0 +1,15 @@
1
+ hexastack_flags/__init__.py,sha256=sILX7G9bFKzH8FXOMtylEyPK2Yqctt8-SGlXDpb9rZ0,331
2
+ hexastack_flags/adapters/__init__.py,sha256=zTMxW_o4w6cjS3GG6RlZXLpuP97mrnR1hJqhuSefops,117
3
+ hexastack_flags/adapters/openfeature.py,sha256=9NWacdLtSMU4OAy9ySBVdG_N_gc_2DvYPnrnZyhrEeE,7114
4
+ hexastack_flags/adapters/providers/__init__.py,sha256=ftKX_Y3qNNNKvLECwHim6Q5QMxXX2f1iTCnfftfLo-Y,141
5
+ hexastack_flags/adapters/providers/factory.py,sha256=TAxSNFMY_q5Ot1tuAH7b2rFvA0AW3wr1FIJglO-au1M,3201
6
+ hexastack_flags/domain/__init__.py,sha256=8oNhqS7YzqFbazlIjazuufiufUVIRZMWM71LAsGGNdE,173
7
+ hexastack_flags/domain/models.py,sha256=7G3MxJc1tWGWQ0OguE6wNp1iheFQm9ErZvIqOcXrULU,852
8
+ hexastack_flags/infra/__init__.py,sha256=M4f5otuwkSMj-R4ocHIoPxdWlJtomJBt5lLBvdKJZN8,256
9
+ hexastack_flags/infra/bootstrap.py,sha256=SEAUYDPWciyiels127MVHN6O1I7paLzTqVVdNgWFYkE,2004
10
+ hexastack_flags/infra/config.py,sha256=Q1OJcJzRk_IFD5vuWN0l9XCq9exywuYEzc8TCq9tZdc,1644
11
+ hexastack_flags/ports/__init__.py,sha256=YqsjRH_ch1AOx0TTNZhJzSez07vvABX-iXTBj6fDG7k,259
12
+ hexastack_flags-0.0.0.dist-info/WHEEL,sha256=EmLkUISDECbcUx3FMCYOqokNOJqNp2r0d4mJzjErvvs,80
13
+ hexastack_flags-0.0.0.dist-info/entry_points.txt,sha256=fQ38RyM0PSxp8D1cP1vRZ-gkNeAg2Gm7KuZEuX1MAdw,91
14
+ hexastack_flags-0.0.0.dist-info/METADATA,sha256=IDKaXc9IKMW2SAzyzQoIsYTrMuLePaS2OQJ9x2E94Z8,2733
15
+ hexastack_flags-0.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.12.3
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [hexastack.bootstrappers]
2
+ flags = hexastack_flags.infra.bootstrap:FeatureFlagBootstrapper
3
+