@evanion/react-feature 0.1.0 β 0.1.2
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.
- package/CHANGELOG.md +45 -0
- package/README.md +11 -34
- package/dist/index.js +2 -4
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,48 @@
|
|
|
1
|
+
## 0.1.2 (2026-11-07)
|
|
2
|
+
|
|
3
|
+
### π Features
|
|
4
|
+
|
|
5
|
+
- β οΈ **react-feature:** re-export the 0.2 condition types ([ac261cfa](https://github.com/Evanion/libraries/commit/ac261cfa))
|
|
6
|
+
|
|
7
|
+
### β οΈ Breaking Changes
|
|
8
|
+
|
|
9
|
+
- **react-feature:** re-export the 0.2 condition types ([ac261cfa](https://github.com/Evanion/libraries/commit/ac261cfa))
|
|
10
|
+
the re-exported `Condition` gains the `exists` and
|
|
11
|
+
`not-exists` ops, so an exhaustive switch over `Condition['op']` stops
|
|
12
|
+
compiling, and `AttributeCondition` and `SerializedAttributeCondition`
|
|
13
|
+
are unions, so code reading `value` narrows on `op` first.
|
|
14
|
+
`ContextSchema.fields` keys are paths, and `RuleOutcome.failed.value` is
|
|
15
|
+
optional.
|
|
16
|
+
|
|
17
|
+
### π§± Updated Dependencies
|
|
18
|
+
|
|
19
|
+
- Updated @evanion/feature to 0.2.0
|
|
20
|
+
|
|
21
|
+
### β€οΈ Thank You
|
|
22
|
+
|
|
23
|
+
- Mikael Pettersson @Evanion
|
|
24
|
+
|
|
25
|
+
## 0.1.1 (2026-11-05)
|
|
26
|
+
|
|
27
|
+
### π Features
|
|
28
|
+
|
|
29
|
+
- **docs:** show a short sample from its own tested region ([68fa945c](https://github.com/Evanion/libraries/commit/68fa945c))
|
|
30
|
+
|
|
31
|
+
### π Documentation
|
|
32
|
+
|
|
33
|
+
- **react-feature:** open on the flags living in your repo ([46ee7f90](https://github.com/Evanion/libraries/commit/46ee7f90))
|
|
34
|
+
- **repo:** link the documentation at libs.evanion.com/docs ([dc18dbb2](https://github.com/Evanion/libraries/commit/dc18dbb2))
|
|
35
|
+
- **react-feature:** open the readme on its family's promise ([66d2eefd](https://github.com/Evanion/libraries/commit/66d2eefd))
|
|
36
|
+
- **react-feature:** give the readme gemma's npm landing-page shape ([c7872824](https://github.com/Evanion/libraries/commit/c7872824))
|
|
37
|
+
|
|
38
|
+
### π§± Updated Dependencies
|
|
39
|
+
|
|
40
|
+
- Updated @evanion/feature to 0.1.1
|
|
41
|
+
|
|
42
|
+
### β€οΈ Thank You
|
|
43
|
+
|
|
44
|
+
- Mikael Pettersson @Evanion
|
|
45
|
+
|
|
1
46
|
## 0.1.0 (2026-10-17)
|
|
2
47
|
|
|
3
48
|
### π Features
|
package/README.md
CHANGED
|
@@ -1,31 +1,8 @@
|
|
|
1
1
|
# @evanion/react-feature
|
|
2
2
|
|
|
3
|
-
**Feature
|
|
3
|
+
**Feature flags that live in your repo. Your own code decides if a feature is onβin your API, your build, and the browserβwith no network call per check.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## The Problem: The "Flags Prop-Drill"
|
|
8
|
-
|
|
9
|
-
When a server resolves flags for a request, the component that actually needs a specific flag is often buried deep in the hierarchy. This creates two primary frictions:
|
|
10
|
-
|
|
11
|
-
1. **Prop-Drilling**: Components that do not use any flags are forced to carry the flags object simply because a distant child requires it.
|
|
12
|
-
2. **Two Answers Per Request**: If a component resolves a flag for itself, it reads its own clock. This means two components in a single render could land on opposite sides of a time window, causing the page to contradict itself.
|
|
13
|
-
|
|
14
|
-
## The Solution: One Resolution, Published
|
|
15
|
-
|
|
16
|
-
`@evanion/feature` resolves every configured feature in one call, and `features.snapshot` states that result as a `DecisionSet` containing the instant of resolution, one decision per key, and the config version wherever the store was built from a document that carries one.
|
|
17
|
-
|
|
18
|
-
`FeatureProvider` publishes that set to the React tree, allowing `useFeature`, `useFeatureEnabled`, `useVariant`, and `useFeatures` to read it. Because the decisions are already made, no hook evaluates anything during the render.
|
|
19
|
-
|
|
20
|
-
### Core Concept: The Server Resolves, The Tree Reads
|
|
21
|
-
|
|
22
|
-
To avoid duplicating complex logic in the UI, `@evanion/react-feature` separates the act of deciding which feature to show from the act of reading that decision.
|
|
23
|
-
|
|
24
|
-
Consider a game shop that splits shoppers between two different shelf-sorting methods. Instead of the UI calculating the split, the server resolves it for a specific shopper via `features.snapshot`, and `FeatureProvider` publishes that set. The `Shelf` component then simply reads its assigned variant. If Sam buckets into `popular` and Mika into `newest`, neither component has to perform the resolution logic themselves.
|
|
25
|
-
|
|
26
|
-
To ensure consistency, the provider takes the shopper's targeting context alongside the set. It publishes the shipped decisions either way, and it re-resolves them against that context to check them; consequently, a provider handed no `targetingKey` reports every weighted assignment as one it cannot reproduce. The context the server resolved with is the context the provider needs.
|
|
27
|
-
|
|
28
|
-
The following block runs in the package's test suite, where each `// ->` comment denotes the expression's value.
|
|
5
|
+
`@evanion/react-feature` is the React adapter for this ecosystem. It allows you to publish the decisions `@evanion/feature` has already resolved to your component tree, enabling any component to ask "is this on, and which variant am I in?" without prop-drilling a flags object through multiple layers of UI.
|
|
29
6
|
|
|
30
7
|
<!-- #region server-render -->
|
|
31
8
|
|
|
@@ -118,16 +95,16 @@ shelfFor('mika'); // -> '<p>Newest first</p>'
|
|
|
118
95
|
|
|
119
96
|
<!-- #endregion server-render -->
|
|
120
97
|
|
|
121
|
-
##
|
|
98
|
+
## Capabilities
|
|
122
99
|
|
|
123
100
|
`@evanion/react-feature` provides the following capabilities:
|
|
124
101
|
|
|
125
|
-
-
|
|
126
|
-
-
|
|
127
|
-
-
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
102
|
+
- **Zero Prop-Drilling**: Components name the key they care about and read the decision from the provider above them.
|
|
103
|
+
- **One Instant Per Render**: A set a render resolved states the instant it was created. The provider reads that instant as the default for anything it resolves itself, ensuring no two components in one tree straddle a time window.
|
|
104
|
+
- **Local Reads**: Hooks read decisions already held by the provider, so variant switches never wait on network requests.
|
|
105
|
+
- **Type-Safety**: `createFeatureContext(features)` returns a provider and hooks bound to one store's schema. Misspelled keys trigger compile errors, and variants are constrained to the union declared by the store.
|
|
106
|
+
- **Divergence Reports**: When handed a shipped set, a provider compares the config version against the store. It names decisions it cannot reproduce via `onDivergence`. Where the set and the store each carry a config version and the two differ, `onVersionMismatch` decides whether the tree renders the shipped set or a local re-resolution. A store built from a bare definitions array carries no version, so a provider over one reports `unversioned` and keeps the shipped set whatever `onVersionMismatch` asks for. Build both sides from a document that carries a version to make the choice available.
|
|
107
|
+
- **Lightweight Footprint**: The package is ESM only and requires Node 20 or newer. Its only peer dependency is React 18 or 19, with `@evanion/feature` pinned as a dependency.
|
|
131
108
|
|
|
132
109
|
## Installation
|
|
133
110
|
|
|
@@ -141,11 +118,11 @@ npm install @evanion/react-feature @evanion/feature
|
|
|
141
118
|
|
|
142
119
|
To master the implementation and explore worked examples, visit the full documentation site:
|
|
143
120
|
|
|
144
|
-
π **[
|
|
121
|
+
π **[libs.evanion.com/docs/react-feature](https://libs.evanion.com/docs/react-feature/)**
|
|
145
122
|
|
|
146
123
|
The `@evanion/react-feature` hooks operate by reading a shared model of features, rules, variants, and decision sets. Because this model is defined in the core package, its detailed documentation is located here:
|
|
147
124
|
|
|
148
|
-
π **[
|
|
125
|
+
π **[libs.evanion.com/docs/feature](https://libs.evanion.com/docs/feature/)**
|
|
149
126
|
|
|
150
127
|
## License
|
|
151
128
|
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createContext as e, useContext as t, useMemo as n, useState as r } from "react";
|
|
3
3
|
import { adoptDecisions as i, reportDivergence as a } from "@evanion/feature";
|
|
4
4
|
import { jsx as o } from "react/jsx-runtime";
|
|
5
|
-
|
|
5
|
+
// src/hydrate.ts
|
|
6
6
|
function s() {
|
|
7
7
|
try {
|
|
8
8
|
return process.env.NODE_ENV === "production";
|
|
@@ -10,8 +10,7 @@ function s() {
|
|
|
10
10
|
return !0;
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
//#region src/index.tsx
|
|
13
|
+
// src/index.tsx
|
|
15
14
|
function c(e) {
|
|
16
15
|
return e;
|
|
17
16
|
}
|
|
@@ -114,5 +113,4 @@ function S(t) {
|
|
|
114
113
|
}
|
|
115
114
|
};
|
|
116
115
|
}
|
|
117
|
-
//#endregion
|
|
118
116
|
export { h as FeatureProvider, S as createFeatureContext, _ as useFeature, v as useFeatureEnabled, g as useFeatures, y as useVariant };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evanion/react-feature",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "The React binding for @evanion/feature: a provider and hooks over an already-built feature store.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"provenance": true
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@evanion/feature": "0.
|
|
57
|
+
"@evanion/feature": "0.2.0"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"react": "^18.0.0 || ^19.0.0"
|