Compare commits

...

170 Commits
v0.1.0 ... main

Author SHA1 Message Date
renovate[bot] 969e11c4d5
chore(deps): update dependency python to 3.13 (#332)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [python](https://redirect.github.com/actions/python-versions) |
uses-with | minor | `3.12` -> `3.13` |

---

### Release Notes

<details>
<summary>actions/python-versions (python)</summary>

###
[`v3.13.6`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.6-16792117939):
3.13.6

[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.5-15601068749...3.13.6-16792117939)

Python 3.13.6

###
[`v3.13.5`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.5-15601068749):
3.13.5

[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.4-15433317575...3.13.5-15601068749)

Python 3.13.5

###
[`v3.13.4`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.4-15433317575):
3.13.4

[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.3-14344076652...3.13.4-15433317575)

Python 3.13.4

###
[`v3.13.3`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.3-14344076652):
3.13.3

[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.2-13708744326...3.13.3-14344076652)

Python 3.13.3

###
[`v3.13.2`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.2-13708744326):
3.13.2

[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.1-13437882550...3.13.2-13708744326)

Python 3.13.2

###
[`v3.13.1`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.1-13437882550):
3.13.1

[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.0-13707372259...3.13.1-13437882550)

Python 3.13.1

###
[`v3.13.0`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.0-13707372259):
3.13.0

[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.12.11-15433310049...3.13.0-13707372259)

Python 3.13.0

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS42MC40IiwidXBkYXRlZEluVmVyIjoiNDEuNjAuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsicmVub3ZhdGUiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-10 17:36:49 +00:00
Michael Beemer c4babd3051
docs: clarify evaluation context in hook requirements (#330)
## This PR

- clarify evaluation context in hook requirements

### Related Issues

Fixes #328

### Notes

I added the clarification in the non-normative section. Hopefully, this
addresses the ambiguity around what we mean by `evaluation context`.

Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2025-08-04 15:46:28 -04:00
Michael Beemer baec39b3fe
chore: address markdown lint issues (#327)
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2025-07-21 14:10:01 -04:00
Todd Baert 2d4b27b116
feat: add point 1.6.2, 2.5.3 about shutdown details (#323)
This is a change that I suspect will have little practical impact for
most users, but helps disambiguate some behavior that recently caused
[issues](https://github.com/open-feature/go-sdk/issues/397) and
[confusion](https://github.com/open-feature/go-sdk/pull/400/files#r2191214490).

This is only one means of addressing this concern, so please don't
hesitate to put forward alternative proposals.

"Idempotent" may not be exactly the right word here, since it's only
idempotent within the scope of one execution of the life-cycle, so I'm
open to copy changes here as well.

# EDIT

⚠️ I've substantially changed this PR based on feedback, and
dismissed all approvals:

As @erka and others have pointed out, practically, `shutdown` has been
used for more than just cleaning up providers, it's also used frequently
to "reset" the API for testing purposes. I think this is a valid
use-case and I don't see any reason why it can't be added to what (at
least my understanding of) the original intent of the function was... so
I've changed 1.6.2 to say that the `shutdown` now also resets the state
of the API fully (removes hooks, providers, event handlers, etc) from
the API.

As @chrfwow noted (and I was concerned about as well) guaranteeing that
`shutdown` is not called while a provider is still starting up will be
tricky to implement. Instead I've changed 1.6.1 to say we will
unconditionally run shutdown on all providers, and also added a
recommendation that providers handle this gracefully in the provider
spec.

@sahidvelji I've also added a pre-amble about shutdown in general as you
requested.

@lukas-reining @erka @beeme1mr @sahidvelji @chrfwow  please re-review.

---------

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Sahid Velji <sahidvelji@gmail.com>
2025-07-16 09:25:26 -04:00
Jonathan Norris 1e98b79719
docs(multi-provider): Document Track Method Support in Multi-Provider (#320)
## This PR

This PR updates the Multi-Provider documentation in the OpenFeature
specification to document the new `track` method support that was added
in [PR #1323](https://github.com/open-feature/js-sdk-contrib/pull/1323).

### 📚 New Documentation Sections

- **Track Method Support**: Added a section explaining how the
Multi-Provider implements tracking functionality
- **Introduction Section**: Updated use case examples to highlight
tracking capabilities
- **BaseEvaluationStrategy**: Added `shouldTrackWithThisProvider` method
to the abstract class

## Code Examples Added

```typescript
// Basic tracking usage
const multiProvider = new MultiProvider([
  { provider: new ProviderA() },
  { provider: new ProviderB() }
])

await OpenFeature.setProviderAndWait(multiProvider)
const client = OpenFeature.getClient()

// Track events across all ready providers
client.track('purchase', { targetingKey: 'user123' }, { value: 99.99, currency: 'USD' })
```

---------

Signed-off-by: Jonathan Norris <jonathan@taplytics.com>
2025-07-11 13:49:19 -04:00
Michael Beemer 224b26e44e
chore: add semcon value release info to appendix d (#324)
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2025-07-10 13:37:33 -04:00
renovate[bot] a3678719bf
chore(deps): update dependency prettier to v3.6.2 (#322)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [prettier](https://prettier.io)
([source](https://redirect.github.com/prettier/prettier)) | [`3.6.1` ->
`3.6.2`](https://renovatebot.com/diffs/npm/prettier/3.6.1/3.6.2) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/prettier/3.6.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/prettier/3.6.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/prettier/3.6.1/3.6.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/prettier/3.6.1/3.6.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>prettier/prettier (prettier)</summary>

###
[`v3.6.2`](https://redirect.github.com/prettier/prettier/blob/HEAD/CHANGELOG.md#362)

[Compare
Source](https://redirect.github.com/prettier/prettier/compare/3.6.1...3.6.2)


[diff](https://redirect.github.com/prettier/prettier/compare/3.6.1...3.6.2)

##### Markdown: Add missing blank line around code block
([#&#8203;17675](https://redirect.github.com/prettier/prettier/pull/17675)
by [@&#8203;fisker](https://redirect.github.com/fisker))

<!-- prettier-ignore -->

````md
<!-- Input -->
1. Some text, and code block below, with newline after code block

   ```yaml
   ---
   foo: bar
   ```

   1. Another
   2. List

<!-- Prettier 3.6.1 -->
1. Some text, and code block below, with newline after code block

   ```yaml
   ---
   foo: bar
   ```
   1. Another
   2. List

<!-- Prettier 3.6.2 -->
1. Some text, and code block below, with newline after code block

   ```yaml
   ---
   foo: bar
   ```

   1. Another
   2. List
````

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsicmVub3ZhdGUiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-30 14:02:44 +00:00
renovate[bot] 705d086779
chore(deps): update dependency prettier to v3.6.1 (#321)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [prettier](https://prettier.io)
([source](https://redirect.github.com/prettier/prettier)) | [`3.6.0` ->
`3.6.1`](https://renovatebot.com/diffs/npm/prettier/3.6.0/3.6.1) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/prettier/3.6.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/prettier/3.6.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/prettier/3.6.0/3.6.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/prettier/3.6.0/3.6.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>prettier/prettier (prettier)</summary>

###
[`v3.6.1`](https://redirect.github.com/prettier/prettier/blob/HEAD/CHANGELOG.md#361)

[Compare
Source](https://redirect.github.com/prettier/prettier/compare/3.6.0...3.6.1)


[diff](https://redirect.github.com/prettier/prettier/compare/3.6.0...3.6.1)

##### TypeScript: Allow const without initializer
([#&#8203;17650](https://redirect.github.com/prettier/prettier/pull/17650),
[#&#8203;17654](https://redirect.github.com/prettier/prettier/pull/17654)
by [@&#8203;fisker](https://redirect.github.com/fisker))

<!-- prettier-ignore -->

```jsx
// Input
export const version: string;

// Prettier 3.6.0 (--parser=babel-ts)
SyntaxError: Unexpected token (1:21)
> 1 | export const version: string;
    |                     ^

// Prettier 3.6.0 (--parser=oxc-ts)
SyntaxError: Missing initializer in const declaration (1:14)
> 1 | export const version: string;
    |              ^^^^^^^^^^^^^^^

// Prettier 3.6.1
export const version: string;
```

##### Miscellaneous: Avoid closing files multiple times
([#&#8203;17665](https://redirect.github.com/prettier/prettier/pull/17665)
by [@&#8203;43081j](https://redirect.github.com/43081j))

When reading a file to infer the interpreter from a shebang, we use the
`n-readlines` library to read the first line in order to get the
shebang.

This library closes files when it reaches EOF, and we later try close
the same
files again. We now close files only if `n-readlines` did not already
close
them.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsicmVub3ZhdGUiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-28 14:45:53 +00:00
renovate[bot] c37ac17c80
chore(deps): update dependency prettier to v3.6.0 (#319)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [prettier](https://prettier.io)
([source](https://redirect.github.com/prettier/prettier)) | [`3.5.3` ->
`3.6.0`](https://renovatebot.com/diffs/npm/prettier/3.5.3/3.6.0) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/prettier/3.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/prettier/3.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/prettier/3.5.3/3.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/prettier/3.5.3/3.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>prettier/prettier (prettier)</summary>

###
[`v3.6.0`](https://redirect.github.com/prettier/prettier/blob/HEAD/CHANGELOG.md#360)

[Compare
Source](https://redirect.github.com/prettier/prettier/compare/3.5.3...3.6.0)


[diff](https://redirect.github.com/prettier/prettier/compare/3.5.3...3.6.0)

🔗 [Release Notes](https://prettier.io/blog/2025/06/23/3.6.0)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsicmVub3ZhdGUiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-26 13:36:17 +00:00
Todd Baert 1965aae810
fix: correct shutdown points after status removal (#317)
This is a minor point of clarification with no material changes.

When we removed `status` from the providers, we didn't migrate this
point to the evaluation API, as @fabriziodemaria pointed out
[here](https://github.com/open-feature/spec/issues/270).

Fixes: https://github.com/open-feature/spec/issues/270

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2025-06-23 13:10:32 -04:00
Todd Baert 42340bb9f5
fix: clarify hook ordering (#315)
This clarifies some ambiguity in hook execution order, mentioned in
https://github.com/open-feature/spec/issues/164:

> The issue here is that the overall hook execution order would vary if
any of these hooks have more than one entry. For example:
> 
> ```
> API = [A, B]
> Client = [C, D]
> Invocation = [E, F]
> Provider = [G, H]
> 
> # `before` list
> before_hooks = API + Client + Invocation + Provider
> 
> # spreading directly (like Python)
> list1 = Provider + Invocation + Client + API
> # results in [G, H, E, F, C, D, A, B]
> 
> # reversing the before list (like JavaScript)
> list2 = before_hooks[:]
> list2.reverse()
> # results in [H, G, F, E, D, C, B, A]
> ```

This change makes clear that `[H, G, F, E, D, C, B, A]` is the correct
answer here, which is the more intuitive choice, in my opinion.

This is also one of the few (only?) normative spec points that isn't a
single sentence, but instead has point-form clauses, so I've also
attempted to fix that.

I've work-shopped this with a few people and they interpreted it
correctly, though admittedly, the term "stack-wise" is doing a lot of
work here. I'm hopeful that our audience is familiar enough with that
sort of term that the intent is clear.

I believe there's a few SDKs where this is incorrectly implemented, I'll
audit those and create issues if this is merged. I don't expect that
such an implementation change will significantly impact anyone.

---------

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2025-06-11 12:11:23 -04:00
Sahid Velji cbfa0a9a78
docs: explicitly mark the Hooks section experimental (#312)
<!-- Please use this template for your pull request. -->
<!-- Please use the sections that you need and delete other sections -->

## This PR
<!-- add the description of the PR here -->

Even though the README says that no explicit status implies experimental
status, I think we should explicitly mark the Hooks section experimental
to align with the other sections.

### Related Issues
<!-- add here the GitHub issue that this PR resolves if applicable -->



### Notes
<!-- any additional notes for this PR -->

### Follow-up Tasks
<!-- anything that is related to this PR but not done here should be
noted under this section -->
<!-- if there is a need for a new issue, please link it here -->

### How to test
<!-- if applicable, add testing instructions under this section -->

Signed-off-by: Sahid Velji <sahidvelji@gmail.com>
2025-06-09 12:42:46 -04:00
Yoneo Arai bb2dc2ce32
Fix header for requirements (#311)
Signed-off-by: Yoneo Arai <yoneo.arai@split.io>
2025-06-05 12:15:57 -04:00
Sahid Velji f0148060e6
docs: fix typos (#310)
Signed-off-by: Sahid Velji <sahid.velji@capitalone.com>
2025-05-23 13:05:35 -04:00
Michael Beemer edf0debe0b
Add a codeowner, set TSC as the spec owner. (#308)
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2025-05-13 17:01:37 -04:00
Michael Beemer c66684f030
feat!: update the observability appendix to include the latest otel semcon (#306)
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2025-05-13 16:45:24 -04:00
Ryan Lamb d27e000b6c
fix: Broaden definition of hook data value types. (#307)
## This PR
Loosens the value definition for hook data.

In the example within the spec, and within the implementation in the
dotnet-sdk, the intent was to allow any type of data. For example
creating an open telemetry span in before, storing it hook data, and
ending it in after.

### Notes
Related to: https://github.com/open-feature/dotnet-sdk/pull/387

---------

Signed-off-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>
2025-04-24 18:57:14 -04:00
renovate[bot] 2ba05d89b5
chore(deps): update dependency markdownlint-cli to ^0.44.0 (#291)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
|
[markdownlint-cli](https://redirect.github.com/igorshubovych/markdownlint-cli)
| [`^0.43.0` ->
`^0.44.0`](https://renovatebot.com/diffs/npm/markdownlint-cli/0.43.0/0.44.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/markdownlint-cli/0.44.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/markdownlint-cli/0.44.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/markdownlint-cli/0.43.0/0.44.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/markdownlint-cli/0.43.0/0.44.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>igorshubovych/markdownlint-cli (markdownlint-cli)</summary>

###
[`v0.44.0`](https://redirect.github.com/igorshubovych/markdownlint-cli/releases/tag/v0.44.0)

[Compare
Source](https://redirect.github.com/igorshubovych/markdownlint-cli/compare/v0.43.0...v0.44.0)

-   Update `markdownlint` dependency to `0.37.4`
    -   Convert module to ECMAScript (breaking change)
    -   Stop using `require`, convert to `import`
    -   Improve `MD032`
-   Update all dependencies via `Dependabot`

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xMjUuMSIsInVwZGF0ZWRJblZlciI6IjM5LjEyNS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-24 11:21:40 -04:00
Todd Baert a5ba017a9c
chore: delete dupe CODEOWNERS
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2025-04-24 11:06:29 -04:00
renovate[bot] 36944c68dd
chore(deps): update dependency node to v22 (#300)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [node](https://redirect.github.com/actions/node-versions) | uses-with
| major | `18` -> `22` |

---

### Release Notes

<details>
<summary>actions/node-versions (node)</summary>

###
[`v22.14.0`](https://redirect.github.com/actions/node-versions/releases/tag/22.14.0-13265982013):
22.14.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.13.1-12900459766...22.14.0-13265982013)

Node.js 22.14.0

###
[`v22.13.1`](https://redirect.github.com/actions/node-versions/releases/tag/22.13.1-12900459766):
22.13.1

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.13.0-12671059536...22.13.1-12900459766)

Node.js 22.13.1

###
[`v22.13.0`](https://redirect.github.com/actions/node-versions/releases/tag/22.13.0-12671059536):
22.13.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.12.0-12152383658...22.13.0-12671059536)

Node.js 22.13.0

###
[`v22.12.0`](https://redirect.github.com/actions/node-versions/releases/tag/22.12.0-12152383658):
22.12.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.11.0-11593095476...22.12.0-12152383658)

Node.js 22.12.0

###
[`v22.11.0`](https://redirect.github.com/actions/node-versions/releases/tag/22.11.0-11593095476):
22.11.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.10.0-11377615849...22.11.0-11593095476)

Node.js 22.11.0

###
[`v22.10.0`](https://redirect.github.com/actions/node-versions/releases/tag/22.10.0-11377615849):
22.10.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.9.0-10914884886...22.10.0-11377615849)

Node.js 22.10.0

###
[`v22.9.0`](https://redirect.github.com/actions/node-versions/releases/tag/22.9.0-10914884886):
22.9.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.8.0-10685632420...22.9.0-10914884886)

Node.js 22.9.0

###
[`v22.8.0`](https://redirect.github.com/actions/node-versions/releases/tag/22.8.0-10685632420):
22.8.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.7.0-10511334152...22.8.0-10685632420)

Node.js 22.8.0

###
[`v22.7.0`](https://redirect.github.com/actions/node-versions/releases/tag/22.7.0-10511334152):
22.7.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.6.0-10277432289...22.7.0-10511334152)

Node.js 22.7.0

###
[`v22.6.0`](https://redirect.github.com/actions/node-versions/releases/tag/22.6.0-10277432289):
22.6.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.5.1-10010673511...22.6.0-10277432289)

Node.js 22.6.0

###
[`v22.5.1`](https://redirect.github.com/actions/node-versions/releases/tag/22.5.1-10010673511):
22.5.1

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.5.0-9985144103...22.5.1-10010673511)

Node.js 22.5.1

###
[`v22.5.0`](https://redirect.github.com/actions/node-versions/releases/tag/22.5.0-9985144103):
22.5.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.4.1-9860948056...22.5.0-9985144103)

Node.js 22.5.0

###
[`v22.4.1`](https://redirect.github.com/actions/node-versions/releases/tag/22.4.1-9860948056):
22.4.1

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.4.0-9766506602...22.4.1-9860948056)

Node.js 22.4.1

###
[`v22.4.0`](https://redirect.github.com/actions/node-versions/releases/tag/22.4.0-9766506602):
22.4.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.3.0-9569309553...22.4.0-9766506602)

Node.js 22.4.0

###
[`v22.3.0`](https://redirect.github.com/actions/node-versions/releases/tag/22.3.0-9569309553):
22.3.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.2.0-9105861751...22.3.0-9569309553)

Node.js 22.3.0

###
[`v22.2.0`](https://redirect.github.com/actions/node-versions/releases/tag/22.2.0-9105861751):
22.2.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.1.0-8926142033...22.2.0-9105861751)

Node.js 22.2.0

###
[`v22.1.0`](https://redirect.github.com/actions/node-versions/releases/tag/22.1.0-8926142033):
22.1.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/22.0.0-8879734543...22.1.0-8926142033)

Node.js 22.1.0

###
[`v22.0.0`](https://redirect.github.com/actions/node-versions/releases/tag/22.0.0-8879734543):
22.0.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.18.3-13244516310...22.0.0-8879734543)

Node.js 22.0.0

###
[`v20.18.3`](https://redirect.github.com/actions/node-versions/releases/tag/20.18.3-13244516310):
20.18.3

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.18.2-12900461873...20.18.3-13244516310)

Node.js 20.18.3

###
[`v20.18.2`](https://redirect.github.com/actions/node-versions/releases/tag/20.18.2-12900461873):
20.18.2

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.18.1-11936931582...20.18.2-12900461873)

Node.js 20.18.2

###
[`v20.18.1`](https://redirect.github.com/actions/node-versions/releases/tag/20.18.1-11936931582):
20.18.1

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.18.0-11182621166...20.18.1-11936931582)

Node.js 20.18.1

###
[`v20.18.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.18.0-11182621166):
20.18.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.17.0-10501110630...20.18.0-11182621166)

Node.js 20.18.0

###
[`v20.17.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.17.0-10501110630):
20.17.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.16.0-10080284600...20.17.0-10501110630)

Node.js 20.17.0

###
[`v20.16.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.16.0-10080284600):
20.16.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.15.1-9860950555...20.16.0-10080284600)

Node.js 20.16.0

###
[`v20.15.1`](https://redirect.github.com/actions/node-versions/releases/tag/20.15.1-9860950555):
20.15.1

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.15.0-9607683765...20.15.1-9860950555)

Node.js 20.15.1

###
[`v20.15.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.15.0-9607683765):
20.15.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.14.0-9279690001...20.15.0-9607683765)

Node.js 20.15.0

###
[`v20.14.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.14.0-9279690001):
20.14.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.13.1-9021092103...20.14.0-9279690001)

Node.js 20.14.0

###
[`v20.13.1`](https://redirect.github.com/actions/node-versions/releases/tag/20.13.1-9021092103):
20.13.1

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.13.0-8995628607...20.13.1-9021092103)

Node.js 20.13.1

###
[`v20.13.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.13.0-8995628607):
20.13.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.12.2-8647736879...20.13.0-8995628607)

Node.js 20.13.0

###
[`v20.12.2`](https://redirect.github.com/actions/node-versions/releases/tag/20.12.2-8647736879):
20.12.2

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.12.1-8541409420...20.12.2-8647736879)

Node.js 20.12.2

###
[`v20.12.1`](https://redirect.github.com/actions/node-versions/releases/tag/20.12.1-8541409420):
20.12.1

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.12.0-8446029429...20.12.1-8541409420)

Node.js 20.12.1

###
[`v20.12.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.12.0-8446029429):
20.12.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.11.1-7910924545...20.12.0-8446029429)

Node.js 20.12.0

###
[`v20.11.1`](https://redirect.github.com/actions/node-versions/releases/tag/20.11.1-7910924545):
20.11.1

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.11.0-7531642937...20.11.1-7910924545)

Node.js 20.11.1

###
[`v20.11.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.11.0-7531642937):
20.11.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.10.0-6972104774...20.11.0-7531642937)

Node.js 20.11.0

###
[`v20.10.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.10.0-6972104774):
20.10.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.9.0-6797996195...20.10.0-6972104774)

Node.js 20.10.0

###
[`v20.9.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.9.0-6797996195):
20.9.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.8.1-6529388730...20.9.0-6797996195)

Node.js 20.9.0

###
[`v20.8.1`](https://redirect.github.com/actions/node-versions/releases/tag/20.8.1-6529388730):
20.8.1

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.8.0-6353460105...20.8.1-6529388730)

Node.js 20.8.1

###
[`v20.8.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.8.0-6353460105):
20.8.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.7.0-6231175880...20.8.0-6353460105)

Node.js 20.8.0

###
[`v20.7.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.7.0-6231175880):
20.7.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.6.1-6142062498...20.7.0-6231175880)

Node.js 20.7.0

###
[`v20.6.1`](https://redirect.github.com/actions/node-versions/releases/tag/20.6.1-6142062498):
20.6.1

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.6.0-6092715008...20.6.1-6142062498)

Node.js 20.6.1

###
[`v20.6.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.6.0-6092715008):
20.6.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.5.1-5819736097...20.6.0-6092715008)

Node.js 20.6.0

###
[`v20.5.1`](https://redirect.github.com/actions/node-versions/releases/tag/20.5.1-5819736097):
20.5.1

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.5.0-5619267642...20.5.1-5819736097)

Node.js 20.5.1

###
[`v20.5.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.5.0-5619267642):
20.5.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.4.0-5484146983...20.5.0-5619267642)

Node.js 20.5.0

###
[`v20.4.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.4.0-5484146983):
20.4.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.3.1-5342956167...20.4.0-5484146983)

Node.js 20.4.0

###
[`v20.3.1`](https://redirect.github.com/actions/node-versions/releases/tag/20.3.1-5342956167):
20.3.1

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.3.0-5218671512...20.3.1-5342956167)

Node.js 20.3.1

###
[`v20.3.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.3.0-5218671512):
20.3.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.2.0-4994802831...20.3.0-5218671512)

Node.js 20.3.0

###
[`v20.2.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.2.0-4994802831):
20.2.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.1.0-4890533026...20.2.0-4994802831)

Node.js 20.2.0

###
[`v20.1.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.1.0-4890533026):
20.1.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/20.0.0-4805425216...20.1.0-4890533026)

Node.js 20.1.0

###
[`v20.0.0`](https://redirect.github.com/actions/node-versions/releases/tag/20.0.0-4805425216):
20.0.0

[Compare
Source](https://redirect.github.com/actions/node-versions/compare/18.20.7-13438827950...20.0.0-4805425216)

Node.js 20.0.0

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOTQuMSIsInVwZGF0ZWRJblZlciI6IjM5LjE5NC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-23 14:03:38 -04:00
Jonathan Norris 18cde1708e
chore: update multi-provider spec docs (#305)
Signed-off-by: Jonathan Norris <jonathan@taplytics.com>
2025-04-14 13:29:27 -04:00
alexandraoberaigner 27e4461b45
fix(docs): change tracking code example to use proper java syntax (#299)
## This PR
just fixes the java syntax in the providers tracking code example

Signed-off-by: Alexandra Oberaigner <alexandra.oberaigner@dynatrace.com>
2025-03-24 19:52:34 -04:00
André Silva 130df3eb61
fix: Clarify scenario descriptions in context merging feature (#304)
<!-- Please use this template for your pull request. -->
<!-- Please use the sections that you need and delete other sections -->

## This PR
<!-- add the description of the PR here -->

This pull request includes updates to the
`specification/assets/gherkin/contextMerging.feature` file to improve
the clarity of scenario descriptions by specifying the context
(transaction, hook, or both) in which the context entries are added or
overwritten.

Changes to scenario descriptions:

* Updated the scenario description to specify that the context entry is
added for a transaction.
* Updated the scenario description to specify that the context entry is
added for a hook.
* Updated the scenario description to specify that the context entry is
added for both a transaction and a hook.
* Updated the scenario outline description to specify that the context
entry overwrites values for a transaction.
* Updated the scenario outline description to specify that the context
entry overwrites values for a hook.
* Updated the scenario outline description to specify that the context
entry overwrites values for both a transaction and a hook.

### Related Issues
<!-- add here the GitHub issue that this PR resolves if applicable -->

Fixes #303

Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
2025-03-24 12:56:03 -04:00
renovate[bot] aad6193d77
chore(deps): update dependency markdown-link-check to v3.13.7 (#301)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
|
[markdown-link-check](https://redirect.github.com/tcort/markdown-link-check)
| [`3.12.2` ->
`3.13.7`](https://renovatebot.com/diffs/npm/markdown-link-check/3.12.2/3.13.7)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/markdown-link-check/3.13.7?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/markdown-link-check/3.13.7?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/markdown-link-check/3.12.2/3.13.7?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/markdown-link-check/3.12.2/3.13.7?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>tcort/markdown-link-check (markdown-link-check)</summary>

###
[`v3.13.7`](https://redirect.github.com/tcort/markdown-link-check/blob/HEAD/CHANGELOG.md#Version-3137)

[Compare
Source](https://redirect.github.com/tcort/markdown-link-check/compare/v3.13.6...v3.13.7)

- Do not override reporter option if not defined in config file (issue
[#&#8203;369](https://redirect.github.com/tcort/markdown-link-check/issues/369))
by [@&#8203;smainil](https://redirect.github.com/smainil) in
[https://github.com/tcort/markdown-link-check/pull/372](https://redirect.github.com/tcort/markdown-link-check/pull/372)
- chore(deps-dev): bump eslint from 9.14.0 to 9.20.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/408](https://redirect.github.com/tcort/markdown-link-check/pull/408)
- chore(deps-dev): bump
[@&#8203;eslint/js](https://redirect.github.com/eslint/js) from 9.14.0
to 9.20.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/407](https://redirect.github.com/tcort/markdown-link-check/pull/407)
- chore(deps-dev): bump express from 4.21.1 to 4.21.2 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/383](https://redirect.github.com/tcort/markdown-link-check/pull/383)
- chore(deps): bump proxy-agent from 6.4.0 to 6.5.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/384](https://redirect.github.com/tcort/markdown-link-check/pull/384)
- chore(deps): bump chalk from 5.3.0 to 5.4.1 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/392](https://redirect.github.com/tcort/markdown-link-check/pull/392)
- chore(deps-dev): bump globals from 15.12.0 to 15.14.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/391](https://redirect.github.com/tcort/markdown-link-check/pull/391)
- chore(deps-dev): bump
[@&#8203;eslint/js](https://redirect.github.com/eslint/js) from 9.20.0
to 9.21.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/414](https://redirect.github.com/tcort/markdown-link-check/pull/414)
- chore(deps-dev): bump mocha from 10.8.2 to 11.1.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/399](https://redirect.github.com/tcort/markdown-link-check/pull/399)
- chore(deps): bump commander from 12.1.0 to 13.1.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/400](https://redirect.github.com/tcort/markdown-link-check/pull/400)
- chore(deps-dev): bump eslint from 9.20.0 to 9.21.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/419](https://redirect.github.com/tcort/markdown-link-check/pull/419)
- chore(deps-dev): bump globals from 15.14.0 to 16.0.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/418](https://redirect.github.com/tcort/markdown-link-check/pull/418)
- chore(deps-dev): bump
[@&#8203;eslint/js](https://redirect.github.com/eslint/js) from 9.21.0
to 9.22.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/426](https://redirect.github.com/tcort/markdown-link-check/pull/426)
- chore(deps-dev): bump eslint from 9.21.0 to 9.22.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/425](https://redirect.github.com/tcort/markdown-link-check/pull/425)

###
[`v3.13.6`](https://redirect.github.com/tcort/markdown-link-check/blob/HEAD/CHANGELOG.md#Version-3136)

[Compare
Source](https://redirect.github.com/tcort/markdown-link-check/compare/v3.13.5...v3.13.6)

-   fix: move script back to its original location

###
[`v3.13.5`](https://redirect.github.com/tcort/markdown-link-check/blob/HEAD/CHANGELOG.md#Version-3135)

[Compare
Source](https://redirect.github.com/tcort/markdown-link-check/compare/v3.13.4...v3.13.5)

- fix: MODULE_NOT_FOUND error
[#&#8203;368](https://redirect.github.com/tcort/markdown-link-check/issues/368)

###
[`v3.13.4`](https://redirect.github.com/tcort/markdown-link-check/blob/HEAD/CHANGELOG.md#Version-3134)

[Compare
Source](https://redirect.github.com/tcort/markdown-link-check/compare/v3.13.3...v3.13.4)

- fix: MODULE_NOT_FOUND error
[#&#8203;368](https://redirect.github.com/tcort/markdown-link-check/issues/368)

###
[`v3.13.3`](https://redirect.github.com/tcort/markdown-link-check/blob/HEAD/CHANGELOG.md#Version-3133)

[Compare
Source](https://redirect.github.com/tcort/markdown-link-check/compare/v3.13.2...v3.13.3)

- fix: MODULE_NOT_FOUND error
[#&#8203;368](https://redirect.github.com/tcort/markdown-link-check/issues/368)

###
[`v3.13.2`](https://redirect.github.com/tcort/markdown-link-check/blob/HEAD/CHANGELOG.md#Version-3132)

[Compare
Source](https://redirect.github.com/tcort/markdown-link-check/compare/v3.13.1...v3.13.2)

- fix: MODULE_NOT_FOUND error
[#&#8203;368](https://redirect.github.com/tcort/markdown-link-check/issues/368)

###
[`v3.13.1`](https://redirect.github.com/tcort/markdown-link-check/blob/HEAD/CHANGELOG.md#Version-3131)

[Compare
Source](https://redirect.github.com/tcort/markdown-link-check/compare/v3.13.0...v3.13.1)

- fix: MODULE_NOT_FOUND error
[#&#8203;368](https://redirect.github.com/tcort/markdown-link-check/issues/368)

###
[`v3.13.0`](https://redirect.github.com/tcort/markdown-link-check/blob/HEAD/CHANGELOG.md#Version-3130)

[Compare
Source](https://redirect.github.com/tcort/markdown-link-check/compare/v3.12.2...v3.13.0)

- feat: anchor link checks support HTML tags like `<a name="foo"></a>`
[#&#8203;331](https://redirect.github.com/tcort/markdown-link-check/issues/331)
[@&#8203;dklimpel](https://redirect.github.com/dklimpel)
-  add support for additional test reporters
[#&#8203;364](https://redirect.github.com/tcort/markdown-link-check/issues/364)
[@&#8203;dudeofawesome](https://redirect.github.com/dudeofawesome)
- feat: support iterate link check over directories in CLI
[#&#8203;334](https://redirect.github.com/tcort/markdown-link-check/issues/334)
[@&#8203;dklimpel](https://redirect.github.com/dklimpel)
- Update hook names
[#&#8203;366](https://redirect.github.com/tcort/markdown-link-check/issues/366)
[@&#8203;henrygerardmoore](https://redirect.github.com/henrygerardmoore)
- chore(deps): remove lodash
[#&#8203;332](https://redirect.github.com/tcort/markdown-link-check/issues/332)
[@&#8203;dklimpel](https://redirect.github.com/dklimpel)
- fix: add used `@eslint/js` to dev dependencies in package.json
[#&#8203;330](https://redirect.github.com/tcort/markdown-link-check/issues/330)
[@&#8203;dklimpel](https://redirect.github.com/dklimpel)
- fix: remove not used const url
[#&#8203;329](https://redirect.github.com/tcort/markdown-link-check/issues/329)
[@&#8203;dklimpel](https://redirect.github.com/dklimpel)
- feat: add support for unicode characters in anchor links
[#&#8203;328](https://redirect.github.com/tcort/markdown-link-check/issues/328)
[@&#8203;dklimpel](https://redirect.github.com/dklimpel)
- docs(readme): explain named regex groups in replacementPatterns
[#&#8203;327](https://redirect.github.com/tcort/markdown-link-check/issues/327)
[@&#8203;AndreyNautilus](https://redirect.github.com/AndreyNautilus)
- ci: bump nodejs
[#&#8203;367](https://redirect.github.com/tcort/markdown-link-check/issues/367)
[@&#8203;dklimpel](https://redirect.github.com/dklimpel)
-   update dependencies

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDAuMCIsInVwZGF0ZWRJblZlciI6IjM5LjIwMC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-14 14:44:00 +00:00
renovate[bot] 09aef37063
chore(deps): update dependency prettier to v3.5.3 (#298)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [prettier](https://prettier.io)
([source](https://redirect.github.com/prettier/prettier)) | [`3.5.2` ->
`3.5.3`](https://renovatebot.com/diffs/npm/prettier/3.5.2/3.5.3) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/prettier/3.5.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/prettier/3.5.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/prettier/3.5.2/3.5.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/prettier/3.5.2/3.5.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>prettier/prettier (prettier)</summary>

###
[`v3.5.3`](https://redirect.github.com/prettier/prettier/compare/3.5.2...b51ba9d46765bcfab714ebca982bd04ad25ae562)

[Compare
Source](https://redirect.github.com/prettier/prettier/compare/3.5.2...3.5.3)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xODUuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE4NS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 06:22:08 +00:00
chrfwow 25c57ee7b6
feat: Add new test suite for context merging (#293)
## This PR
Adds gherkin tests to verify the merging order of contexts:
https://openfeature.dev/specification/sections/evaluation-context/#requirement-323

See https://github.com/open-feature/flagd-testbed/issues/13

---------

Signed-off-by: christian.lutnik <christian.lutnik@dynatrace.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
2025-03-05 21:12:46 -05:00
renovate[bot] 0cd553d85f
chore(deps): update dependency prettier to v3.5.2 (#297)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [prettier](https://prettier.io)
([source](https://redirect.github.com/prettier/prettier)) | [`3.5.1` ->
`3.5.2`](https://renovatebot.com/diffs/npm/prettier/3.5.1/3.5.2) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/prettier/3.5.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/prettier/3.5.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/prettier/3.5.1/3.5.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/prettier/3.5.1/3.5.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>prettier/prettier (prettier)</summary>

###
[`v3.5.2`](https://redirect.github.com/prettier/prettier/blob/HEAD/CHANGELOG.md#352)

[Compare
Source](https://redirect.github.com/prettier/prettier/compare/3.5.1...3.5.2)


[diff](https://redirect.github.com/prettier/prettier/compare/3.5.1...3.5.2)

##### Remove `module-sync` condition
([#&#8203;17156](https://redirect.github.com/prettier/prettier/pull/17156)
by [@&#8203;fisker](https://redirect.github.com/fisker))

In Prettier 3.5.0, [we added `module-sync` condition to
`package.json`](https://prettier.io/blog/2025/02/09/3.5.0#use-esm-entrypoint-for-requireesm-16958-by-tats-u),
so that `require("prettier")` can use ESM version, but turns out it
doesn't work if CommonJS and ESM plugins both imports builtin plugins.
To solve this problem, we decide simply remove the `module-sync`
condition, so `require("prettier")` will still use the CommonJS version,
we'll revisit until `require(ESM)` feature is more stable.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNzYuMiIsInVwZGF0ZWRJblZlciI6IjM5LjE3Ni4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-25 13:36:09 +00:00
renovate[bot] a69f748db2
chore(deps): update dependency prettier to v3.5.1 (#296)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [prettier](https://prettier.io)
([source](https://redirect.github.com/prettier/prettier)) | [`3.5.0` ->
`3.5.1`](https://renovatebot.com/diffs/npm/prettier/3.5.0/3.5.1) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/prettier/3.5.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/prettier/3.5.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/prettier/3.5.0/3.5.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/prettier/3.5.0/3.5.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>prettier/prettier (prettier)</summary>

###
[`v3.5.1`](https://redirect.github.com/prettier/prettier/blob/HEAD/CHANGELOG.md#351)

[Compare
Source](https://redirect.github.com/prettier/prettier/compare/3.5.0...3.5.1)


[diff](https://redirect.github.com/prettier/prettier/compare/3.5.0...3.5.1)

##### Fix CLI crash when cache for old version exists
([#&#8203;17100](https://redirect.github.com/prettier/prettier/pull/17100)
by [@&#8203;sosukesuzuki](https://redirect.github.com/sosukesuzuki))

Prettier 3.5 uses a different cache format than previous versions,
Prettier 3.5.0 crashes when reading existing cache file, Prettier 3.5.1
fixed the problem.

##### Support dockercompose and github-actions-workflow in VSCode
([#&#8203;17101](https://redirect.github.com/prettier/prettier/pull/17101)
by [@&#8203;remcohaszing](https://redirect.github.com/remcohaszing))

Prettier now supports the `dockercompose` and `github-actions-workflow`
languages in Visual Studio Code.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNjcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjE2Ny4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-16 22:34:05 +00:00
renovate[bot] 54952f3b54
chore(deps): update dependency prettier to v3.5.0 (#295)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [prettier](https://prettier.io)
([source](https://redirect.github.com/prettier/prettier)) | [`3.4.2` ->
`3.5.0`](https://renovatebot.com/diffs/npm/prettier/3.4.2/3.5.0) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/prettier/3.5.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/prettier/3.5.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/prettier/3.4.2/3.5.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/prettier/3.4.2/3.5.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>prettier/prettier (prettier)</summary>

###
[`v3.5.0`](https://redirect.github.com/prettier/prettier/blob/HEAD/CHANGELOG.md#350)

[Compare
Source](https://redirect.github.com/prettier/prettier/compare/3.4.2...3.5.0)


[diff](https://redirect.github.com/prettier/prettier/compare/3.4.2...3.5.0)

🔗 [Release Notes](https://prettier.io/blog/2025/02/09/3.5.0.html)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNjcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjE2Ny4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-12 20:03:34 +00:00
chrfwow be56f22af9
noissue: fix the test hook behaviour on type error (#294)
Signed-off-by: christian.lutnik <christian.lutnik@dynatrace.com>
2025-02-10 09:35:49 -05:00
chrfwow 95fe981d9e
Add gherkin tests to verify evaluation details in finally hooks (#290)
Signed-off-by: christian.lutnik <christian.lutnik@dynatrace.com>
2025-02-07 14:06:35 -05:00
Michael Beemer 8d6eeb3247
chore!: remove flag type property, add requirement level and footnotes (#292)
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2025-02-03 11:45:24 -05:00
chrfwow 5b07065985
feat: Update test harness with metadata assertions #1467 (#289)
## This PR
Adds gherkin test to verify that flag evaluations provide metadata

### Related Issues

Part of #1467
([https://github.com/open-feature/flagd/issues/1467](https://github.com/open-feature/flagd/issues/1467))

### Follow-up Tasks
Implement steps of the gherkin file in the repositories, and add test
data according to the issue

---------

Signed-off-by: christian.lutnik <christian.lutnik@dynatrace.com>
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Simon Schrottner <simon.schrottner@dynatrace.com>
2025-01-27 09:10:20 -05:00
Todd Baert 6c673d7716
feat: add appendix D (observability) (#287)
- adds small appendix section to spec that defines how particular SDK
fields are mapped to the recently merge OTel semantics
- this allows providers and hooks to export OTel data consistently for
easy interop
- adds a couple supporting glossary liks

---------

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2025-01-15 14:14:51 -05:00
Ryan Lamb c287b5844a
feat: Add hook-data concept for hooks. (#273)
<!-- Please use this template for your pull request. -->
<!-- Please use the sections that you need and delete other sections -->

## This PR
<!-- add the description of the PR here -->

Add support for the hook-data concept for hooks. Hook-data allows for
per-evaluation data to be propagated between hooks.

This is especially useful for analytics purposes where you may want to
measure things that happen between stages, or you want to do something
like create a span in one stage and close it in another.

This concept is similar to the `series data` concept for LaunchDarkly
hooks.
https://github.com/launchdarkly/open-sdk-specs/tree/main/specs/HOOK-hooks#evaluationseriesdata

Unlike `series data` the data in this approach is mutable. This is
because the `before` stage already has a return value. We could
workaround this by specifying a return structure, but it maybe seems
more complex. The data is only passed to a specific hook instance, so
mutability is not of great concern.

Some functional languages may still need to use an immutable with return
values approach.

I can create an OFEP if we think this merits discussion prior to
proposal.

### Related Issues
<!-- add here the GitHub issue that this PR resolves if applicable -->
Related discussion in a PR comment.

https://github.com/open-feature/java-sdk/pull/1049#discussion_r1718895742

---------

Signed-off-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Co-authored-by: Lukas Reining <lukas.reining@codecentric.de>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
2025-01-15 09:29:09 -05:00
renovate[bot] d261f68331
chore(deps): update dependency markdownlint-cli to ^0.43.0 (#257)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
|
[markdownlint-cli](https://redirect.github.com/igorshubovych/markdownlint-cli)
| [`^0.39.0` ->
`^0.43.0`](https://renovatebot.com/diffs/npm/markdownlint-cli/0.39.0/0.43.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/markdownlint-cli/0.43.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/markdownlint-cli/0.43.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/markdownlint-cli/0.39.0/0.43.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/markdownlint-cli/0.39.0/0.43.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>igorshubovych/markdownlint-cli (markdownlint-cli)</summary>

###
[`v0.43.0`](https://redirect.github.com/igorshubovych/markdownlint-cli/compare/v0.42.0...v0.43.0)

[Compare
Source](https://redirect.github.com/igorshubovych/markdownlint-cli/compare/v0.42.0...v0.43.0)

###
[`v0.42.0`](https://redirect.github.com/igorshubovych/markdownlint-cli/releases/tag/v0.42.0)

[Compare
Source](https://redirect.github.com/igorshubovych/markdownlint-cli/compare/v0.41.0...v0.42.0)

-   Update `markdownlint` dependency to `0.35.0`
    -   Add `MD058`/`blanks-around-tables`
- Use `micromark` in
`MD001`/`MD003`/`MD009`/`MD010`/`MD013`/`MD014`/`MD019`/`MD021`/`MD023`/`MD024`/`MD025`/`MD039`/`MD042`/`MD043`
    -   Improve `MD018`/`MD020`/`MD031`/`MD034`/`MD044`
    -   `markdown-it` parser no longer invoked by default
    -   Improve performance
-   Update all dependencies via `Dependabot`

###
[`v0.41.0`](https://redirect.github.com/igorshubovych/markdownlint-cli/releases/tag/v0.41.0):
0.41.0

[Compare
Source](https://redirect.github.com/igorshubovych/markdownlint-cli/compare/v0.40.0...v0.41.0)

- Change TOML parser to `smol-toml` which supports v1.0.0 of the
specification
-   Update all dependencies via `Dependabot`

###
[`v0.40.0`](https://redirect.github.com/igorshubovych/markdownlint-cli/releases/tag/v0.40.0):
0.40.0

[Compare
Source](https://redirect.github.com/igorshubovych/markdownlint-cli/compare/v0.39.0...v0.40.0)

-   Update `markdownlint` dependency to `0.34.0`
- Use micromark in
`MD027`/`MD028`/`MD036`/`MD040`/`MD041`/`MD046`/`MD048`
    -   Improve `MD013`/`MD034`/`MD049`/`MD050`/`MD051`
-   Add support for `TOML` configuration files via `--config`
-   Add `--configPointer` argument for nested configuration
-   Update `--ignore` for directories to ignore all files within
-   Update all dependencies via `Dependabot`

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4zMzEuMCIsInVwZGF0ZWRJblZlciI6IjM5LjE5LjAiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbInJlbm92YXRlIl19-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-02 10:25:59 -05:00
Nayeon Kim 435b11a9b2
fix: typo in specification_parser (#288)
Signed-off-by: Nayeon Kim <inourbubble2@gmail.com>
2025-01-02 10:23:19 -05:00
renovate[bot] ed0f9ef5a3
chore(deps): update dependency prettier to v3.4.2 (#284)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [prettier](https://prettier.io)
([source](https://redirect.github.com/prettier/prettier)) | [`3.4.1` ->
`3.4.2`](https://renovatebot.com/diffs/npm/prettier/3.4.1/3.4.2) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/prettier/3.4.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/prettier/3.4.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/prettier/3.4.1/3.4.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/prettier/3.4.1/3.4.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>prettier/prettier (prettier)</summary>

###
[`v3.4.2`](https://redirect.github.com/prettier/prettier/blob/HEAD/CHANGELOG.md#342)

[Compare
Source](https://redirect.github.com/prettier/prettier/compare/3.4.1...3.4.2)


[diff](https://redirect.github.com/prettier/prettier/compare/3.4.1...3.4.2)

##### Treat U+30A0 & U+30FB in Katakana Block as CJK
([#&#8203;16796](https://redirect.github.com/prettier/prettier/pull/16796)
by [@&#8203;tats-u](https://redirect.github.com/tats-u))

Prettier doesn't treat U+30A0 & U+30FB as Japanese. U+30FB is commonly
used in Japanese to represent the delimitation of first and last names
of non-Japanese people or “and”. The following “C言語・C++・Go・Rust” means
“C language & C++ & Go & Rust” in Japanese.

<!-- prettier-ignore -->

```md
<!-- Input (--prose-wrap=never) -->

C言
語
・
C++
・
Go
・
Rust

<!-- Prettier 3.4.1 -->
C言語・ C++ ・ Go ・ Rust

<!-- Prettier 3.4.2 -->
C言語・C++・Go・Rust
```

U+30A0 can be used as the replacement of the `-` in non-Japanese names
(e.g. “Saint-Saëns” (Charles Camille Saint-Saëns) can be represented as
“サン゠サーンス” in Japanese), but substituted by ASCII hyphen (U+002D) or
U+FF1D (full width hyphen) in many cases (e.g. “サン=サーンス” or “サン=サーンス”).

##### Fix comments print on class methods with decorators
([#&#8203;16891](https://redirect.github.com/prettier/prettier/pull/16891)
by [@&#8203;fisker](https://redirect.github.com/fisker))

<!-- prettier-ignore -->

```jsx
// Input
class A {
  @&#8203;decorator
  /** 
   * The method description
   *
  */
  async method(foo: Foo, bar: Bar) {
    console.log(foo);
  }
}

// Prettier 3.4.1
class A {
  @&#8203;decorator
  async /**
   * The method description
   *
   */
  method(foo: Foo, bar: Bar) {
    console.log(foo);
  }
}

// Prettier 3.4.2
class A {
  @&#8203;decorator
  /**
   * The method description
   *
   */
  async method(foo: Foo, bar: Bar) {
    console.log(foo);
  }
}
```

##### Fix non-idempotent formatting
([#&#8203;16899](https://redirect.github.com/prettier/prettier/pull/16899)
by [@&#8203;seiyab](https://redirect.github.com/seiyab))

This bug fix is not language-specific. You may see similar change in any
languages. This fixes regression in 3.4.0 so change caused by it should
yield same formatting as 3.3.3.

<!-- prettier-ignore -->

```jsx
// Input
<div>
  foo
  <span>longlonglonglonglonglonglonglonglonglonglonglonglonglonglongl foo</span>
  , abc
</div>;

// Prettier 3.4.1 (first)
<div>
  foo
  <span>
    longlonglonglonglonglonglonglonglonglonglonglonglonglonglongl foo
  </span>, abc
</div>;

// Prettier 3.4.1 (second)
<div>
  foo
  <span>longlonglonglonglonglonglonglonglonglonglonglonglonglonglongl foo</span>
  , abc
</div>;

// Prettier 3.4.2
<div>
  foo
  <span>longlonglonglonglonglonglonglonglonglonglonglonglonglonglongl foo</span>
  , abc
</div>;
```

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS40Mi40IiwidXBkYXRlZEluVmVyIjoiMzkuNDIuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsicmVub3ZhdGUiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-07 10:10:12 +00:00
Michael Beemer 776ee1f396
feat!: add evaluation details to finally hook (#280)
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2024-12-06 15:46:14 +00:00
renovate[bot] d4a9a91094
chore(deps): update dependency prettier to v3.4.1 (#283)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [prettier](https://prettier.io)
([source](https://redirect.github.com/prettier/prettier)) | [`3.4.0` ->
`3.4.1`](https://renovatebot.com/diffs/npm/prettier/3.4.0/3.4.1) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/prettier/3.4.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/prettier/3.4.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/prettier/3.4.0/3.4.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/prettier/3.4.0/3.4.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>prettier/prettier (prettier)</summary>

###
[`v3.4.1`](https://redirect.github.com/prettier/prettier/blob/HEAD/CHANGELOG.md#341)

[Compare
Source](https://redirect.github.com/prettier/prettier/compare/3.4.0...3.4.1)


[diff](https://redirect.github.com/prettier/prettier/compare/3.4.0...3.4.1)

##### Remove unnecessary parentheses around assignment in `v-on`
([#&#8203;16887](https://redirect.github.com/prettier/prettier/pull/16887)
by [@&#8203;fisker](https://redirect.github.com/fisker))

<!-- prettier-ignore -->

```vue
<!-- Input -->
<template>
  <button @&#8203;click="foo += 2">Click</button>
</template>

<!-- Prettier 3.4.0 -->
<template>
  <button @&#8203;click="(foo += 2)">Click</button>
</template>

<!-- Prettier 3.4.1 -->
<template>
  <button @&#8203;click="foo += 2">Click</button>
</template>
```

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOS4wIiwidXBkYXRlZEluVmVyIjoiMzkuMTkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsicmVub3ZhdGUiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-29 15:59:50 +00:00
renovate[bot] 6ecbbfd004
chore(deps): update dependency prettier to v3.4.0 (#282)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [prettier](https://prettier.io)
([source](https://redirect.github.com/prettier/prettier)) | [`3.3.3` ->
`3.4.0`](https://renovatebot.com/diffs/npm/prettier/3.3.3/3.4.0) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/prettier/3.4.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/prettier/3.4.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/prettier/3.3.3/3.4.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/prettier/3.3.3/3.4.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>prettier/prettier (prettier)</summary>

###
[`v3.4.0`](https://redirect.github.com/prettier/prettier/blob/HEAD/CHANGELOG.md#340)

[Compare
Source](https://redirect.github.com/prettier/prettier/compare/3.3.3...3.4.0)


[diff](https://redirect.github.com/prettier/prettier/compare/3.3.3...3.4.0)

🔗 [Release Notes](https://prettier.io/blog/2024/11/26/3.4.0.html)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOS4wIiwidXBkYXRlZEluVmVyIjoiMzkuMTkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsicmVub3ZhdGUiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-29 09:23:30 +00:00
Anton Grübel 7a78eed656
chore: add mypy and pytest CI step (#275)
Signed-off-by: gruebel <anton.gruebel@gmail.com>
2024-11-27 12:17:01 -05:00
Roman Dmytrenko 3c737a6e86
chore: use html entity code for asterisk in mermaid diagram (#278)
Signed-off-by: Roman Dmytrenko <rdmytrenko@gmail.com>
2024-11-04 14:02:09 -05:00
Todd Baert ffebdecd72
fix: clarify in-memory provider reqs (#277)
Minor clarifications to the in-memory provider, especially around `flags
changed` which at the moment is not consistently implemented.

---------

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Co-authored-by: Lukas Reining <lukas.reining@codecentric.de>
2024-10-22 08:35:06 -04:00
Todd Baert cd99c3560a
feat: tracking (#268)
Adds tracking

---------

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Justin Abrahms <justin@abrah.ms>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Co-authored-by: Ron Cohen <cohen1@gmail.com>
Co-authored-by: Robert Kozikowski <r.kozikowski@gmail.com>
Co-authored-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>
Co-authored-by: Nicklas Lundin <nicklaslundin@gmail.com>
2024-10-07 13:46:48 -04:00
Anton Grübel df1f62ee7f
chore: add dedicated Python CI + ruff (#274)
<!-- Please use this template for your pull request. -->
<!-- Please use the sections that you need and delete other sections -->

## This PR
<!-- add the description of the PR here -->

- added a dedicated Python CI workflow
- added ruff for linting (only dependency sorting is enabled for now)
and formatting
- added a couple of missing type hints, enabling mypy comes in a
separate PR

Signed-off-by: gruebel <anton.gruebel@gmail.com>
2024-10-04 10:19:50 -04:00
Todd Baert 807c0d2c8d
chore: replace some pngs with mermaid (#272)
Just replaces some PNGs with mermaid.

This is nice because they can change color for the website, and they are
more rigorous and maintainable (we already use mermaid in the spec).

old:


![image](https://github.com/user-attachments/assets/de15b19b-aa3b-41b7-a264-8c64897de1e1)

new: 

```mermaid
flowchart LR
    A[Application] --> API(Evaluation API)
    API --> P[Provider]
    P --> FMS[(Flag Management System)]
```


![image](https://github.com/user-attachments/assets/f90853f9-7211-4c1a-a10a-e66aeb21b501)

```mermaid
flowchart LR
    B(('Before' stage)) ---> FE[Flag Evaluation]
    B -..->|Error| E
    FE ---> A(('After' stage))
    FE -..->|Error| E(('Error' stage))
    A ---> F(('Finally' stage))
    E -..-> F
```

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2024-09-24 16:30:22 -04:00
Michael Beemer db0df51ce8
add flag set to glossary (#271)
## This PR

- adds flag set to the glossary

### Notes

The term was voted on by the OpenFeature community.

https://github.com/orgs/open-feature/discussions/394

---------

Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
2024-09-20 13:23:56 -04:00
Todd Baert 80ceaa0465
fix: lint
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2024-09-04 12:06:55 -04:00
Todd Baert 7f7830b5d8
fix: underscore instead of space
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2024-09-04 10:35:11 -04:00
Todd Baert 50b09898ca
fixup: typo and improve supporting text in logging hook
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2024-09-04 10:34:29 -04:00
Todd Baert bcb8cf1638
feat: logging requirements and logging hook (#269)
As discussed in recent meetings, we've had complaints about this from
multiple sources. This PR adds a stipulation that no logging is done by
the SDK during flag evaluation. It also defines a very simple `logging
hook` concept as an included utility. These should be very simple to
write and will provide all the needed logging but give authors more
control than built in log statements. It will also be a very nice intro
to the hooks concept for users.

Here's the Java implementation:
https://github.com/open-feature/java-sdk/pull/1084

---------

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2024-09-04 07:40:06 -04:00
renovate[bot] 464bd02924
chore(deps): update dependency prettier to v3.3.3 (#267)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [prettier](https://prettier.io)
([source](https://togithub.com/prettier/prettier)) | [`3.3.2` ->
`3.3.3`](https://renovatebot.com/diffs/npm/prettier/3.3.2/3.3.3) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/prettier/3.3.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/prettier/3.3.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/prettier/3.3.2/3.3.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/prettier/3.3.2/3.3.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>prettier/prettier (prettier)</summary>

###
[`v3.3.3`](https://togithub.com/prettier/prettier/blob/HEAD/CHANGELOG.md#333)

[Compare
Source](https://togithub.com/prettier/prettier/compare/3.3.2...3.3.3)

[diff](https://togithub.com/prettier/prettier/compare/3.3.2...3.3.3)

##### Add parentheses for nullish coalescing in ternary
([#&#8203;16391](https://togithub.com/prettier/prettier/pull/16391) by
[@&#8203;cdignam-segment](https://togithub.com/cdignam-segment))

This change adds clarity to operator precedence.

<!-- prettier-ignore -->

```js
// Input
foo ? bar ?? foo : baz;
foo ?? bar ? a : b;
a ? b : foo ?? bar;

// Prettier 3.3.2
foo ? bar ?? foo : baz;
foo ?? bar ? a : b;
a ? b : foo ?? bar;

// Prettier 3.3.3
foo ? (bar ?? foo) : baz;
(foo ?? bar) ? a : b;
a ? b : (foo ?? bar);
```

##### Add parentheses for decorator expressions
([#&#8203;16458](https://togithub.com/prettier/prettier/pull/16458) by
[@&#8203;y-schneider](https://togithub.com/y-schneider))

Prevent parentheses around member expressions or tagged template
literals from being removed to follow the stricter parsing rules of
TypeScript 5.5.

<!-- prettier-ignore -->

```ts
// Input
@&#8203;(foo`tagged template`)
class X {}

// Prettier 3.3.2
@&#8203;foo`tagged template`
class X {}

// Prettier 3.3.3
@&#8203;(foo`tagged template`)
class X {}
```

##### Support `@let` declaration syntax
([#&#8203;16474](https://togithub.com/prettier/prettier/pull/16474) by
[@&#8203;sosukesuzuki](https://togithub.com/sosukesuzuki))

Adds support for Angular v18 `@let` declaration syntax.

Please see the following code example. The `@let` declaration allows you
to define local variables within the template:

<!-- prettier-ignore -->

```html
@&#8203;let name = 'Frodo';

<h1>Dashboard for {{name}}</h1>
Hello, {{name}}
```

For more details, please refer to the excellent blog post by the Angular
Team: [Introducing @&#8203;let in
Angular](https://blog.angular.dev/introducing-let-in-angular-686f9f383f0f).

We also appreciate the Angular Team for kindly answering our questions
to implement this feature.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy40MzEuNCIsInVwZGF0ZWRJblZlciI6IjM3LjQzMS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-16 20:44:03 +00:00
Adam Wootton eaa44da211
chore: add multi provider appendix (#264)
- Adds an appendix section for the Multi-Provider, describing its
purpose and implementation

---------

Signed-off-by: Adam Wootton <adam@taplytics.com>
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
2024-06-18 15:20:11 -04:00
renovate[bot] 3f133fb78e
chore(deps): update dependency prettier to v3.3.2 (#266)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [prettier](https://prettier.io)
([source](https://togithub.com/prettier/prettier)) | [`3.3.1` ->
`3.3.2`](https://renovatebot.com/diffs/npm/prettier/3.3.1/3.3.2) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/prettier/3.3.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/prettier/3.3.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/prettier/3.3.1/3.3.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/prettier/3.3.1/3.3.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>prettier/prettier (prettier)</summary>

###
[`v3.3.2`](https://togithub.com/prettier/prettier/blob/HEAD/CHANGELOG.md#332)

[Compare
Source](https://togithub.com/prettier/prettier/compare/3.3.1...3.3.2)

[diff](https://togithub.com/prettier/prettier/compare/3.3.1...3.3.2)

##### Fix handlebars path expressions starts with `@`
([#&#8203;16358](https://togithub.com/prettier/prettier/pull/16358) by
[@&#8203;Princeyadav05](https://togithub.com/Princeyadav05))

<!-- prettier-ignore -->

```hbs
{{! Input }}
<div>{{@&#8203;x.y.z}}</div>

{{! Prettier 3.3.1 }}
<div>{{@&#8203;x}}</div>

{{! Prettier 3.3.2 }}
<div>{{@&#8203;x.y.z}}</div>
```

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4zOTMuMCIsInVwZGF0ZWRJblZlciI6IjM3LjM5My4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-14 09:24:56 +00:00
Todd Baert d46e6dce67
feat: tracking (#260)
- Adds section 6: tracking
  - no normative sections
  - overview
  - goals/non-goals (temporary)
  
This is not final! Please review goals/non-goals especially, and add any
questions you might have.

---------

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Co-authored-by: Nicklas Lundin <nicklaslundin@gmail.com>
Co-authored-by: Weyert de Boer <weyert@gmail.com>
2024-06-12 09:57:32 -04:00
renovate[bot] e368a5c10b
chore(deps): update dependency prettier to v3.3.1 (#263)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [prettier](https://prettier.io)
([source](https://togithub.com/prettier/prettier)) | [`3.3.0` ->
`3.3.1`](https://renovatebot.com/diffs/npm/prettier/3.3.0/3.3.1) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/prettier/3.3.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/prettier/3.3.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/prettier/3.3.0/3.3.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/prettier/3.3.0/3.3.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>prettier/prettier (prettier)</summary>

###
[`v3.3.1`](https://togithub.com/prettier/prettier/blob/HEAD/CHANGELOG.md#331)

[Compare
Source](https://togithub.com/prettier/prettier/compare/3.3.0...3.3.1)

[diff](https://togithub.com/prettier/prettier/compare/3.3.0...3.3.1)

##### Preserve empty lines in front matter
([#&#8203;16347](https://togithub.com/prettier/prettier/pull/16347) by
[@&#8203;fisker](https://togithub.com/fisker))

<!-- prettier-ignore -->

```markdown
<!-- Input -->
---
foo:
  - bar1

  - bar2

  - bar3
---
Markdown

<!-- Prettier 3.3.0 -->

---
foo:
  - bar1
  - bar2
  - bar3
---

Markdown

<!-- Prettier 3.3.1 -->
---
foo:
  - bar1

  - bar2

  - bar3
---

Markdown
```

##### Preserve explicit language in front matter
([#&#8203;16348](https://togithub.com/prettier/prettier/pull/16348) by
[@&#8203;fisker](https://togithub.com/fisker))

<!-- prettier-ignore -->

```markdown
<!-- Input -->
---yaml
title: Hello
slug: home
---

<!-- Prettier 3.3.0 -->
---
title: Hello
slug: home
---

<!-- Prettier 3.3.1 -->
---yaml
title: Hello
slug: home
---
```

##### Avoid line breaks in import attributes
([#&#8203;16349](https://togithub.com/prettier/prettier/pull/16349) by
[@&#8203;fisker](https://togithub.com/fisker))

<!-- prettier-ignore -->

```jsx
// Input
import something from "./some-very-very-very-very-very-very-very-very-long-path.json" with { type: "json" };

// Prettier 3.3.0
import something from "./some-very-very-very-very-very-very-very-very-long-path.json" with { type:
  "json" };

// Prettier 3.3.1
import something from "./some-very-very-very-very-very-very-very-very-long-path.json" with { type: "json" };
```

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4zOTMuMCIsInVwZGF0ZWRJblZlciI6IjM3LjM5My4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-08 12:55:59 +00:00
renovate[bot] 8f2fa81b2e
chore(deps): update dependency prettier to v3.3.0 (#262)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [prettier](https://prettier.io)
([source](https://togithub.com/prettier/prettier)) | [`3.2.5` ->
`3.3.0`](https://renovatebot.com/diffs/npm/prettier/3.2.5/3.3.0) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/prettier/3.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/prettier/3.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/prettier/3.2.5/3.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/prettier/3.2.5/3.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>prettier/prettier (prettier)</summary>

###
[`v3.3.0`](https://togithub.com/prettier/prettier/blob/HEAD/CHANGELOG.md#330)

[Compare
Source](https://togithub.com/prettier/prettier/compare/3.2.5...3.3.0)

[diff](https://togithub.com/prettier/prettier/compare/3.2.5...3.3.0)

🔗 [Release Notes](https://prettier.io/blog/2024/06/01/3.3.0.html)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4zODguMSIsInVwZGF0ZWRJblZlciI6IjM3LjM4OC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZSJdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-06 18:32:11 +00:00
Justin Abrahms bc2d6dfc63
feat: Inline comment support for spec repos (#261)
## This PR
This should allow someone to mark specifications as done based on inline
comments, rather than having to hand-roll regexes.

### How to test
set this in your `.specrc` (example is for go)

```cfg
[spec]
file_extension=go
inline_comment_prefix=//
```

then edit code by adding lines like:

```
import (
	"errors"
	"fmt"
	"sync"

	"github.com/go-logr/logr"
	"github.com/open-feature/go-sdk/openfeature/internal"
	"golang.org/x/exp/maps"
)

//  spec:5.3.5:If the provider emits an event, the value of the client's provider status MUST be updated accordingly.:end

// spec:5.3.4.3:If the provider's on context changed function terminates
// abnormally, and no other invocations have yet to terminate, associated
// PROVIDER_ERROR handlers MUST run.:end

// evaluationAPI wraps OpenFeature evaluation API functionalities
type evaluationAPI struct {
	defaultProvider FeatureProvider
	namedProviders  map[string]FeatureProvider
```

and run `python /path/to/spec/tools/repo_parser/spec_finder.py
--code-directory .`

then validate those don't show up in the remaining complaints.

Signed-off-by: Justin Abrahms <justin@abrah.ms>
2024-06-06 12:10:12 -04:00
renovate[bot] c0739a164d
chore(deps): update dependency markdown-link-check to v3.12.2 (#252)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [markdown-link-check](https://togithub.com/tcort/markdown-link-check)
| [`3.11.2` ->
`3.12.2`](https://renovatebot.com/diffs/npm/markdown-link-check/3.11.2/3.12.2)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/markdown-link-check/3.12.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/markdown-link-check/3.12.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/markdown-link-check/3.11.2/3.12.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/markdown-link-check/3.11.2/3.12.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>tcort/markdown-link-check (markdown-link-check)</summary>

###
[`v3.12.2`](https://togithub.com/tcort/markdown-link-check/releases/tag/v3.12.2)

[Compare
Source](https://togithub.com/tcort/markdown-link-check/compare/v3.12.1...v3.12.2)

#### What's Changed

- fix status badge in README by
[@&#8203;dklimpel](https://togithub.com/dklimpel) in
[https://github.com/tcort/markdown-link-check/pull/303](https://togithub.com/tcort/markdown-link-check/pull/303)
- enable skipped tests for hash links by
[@&#8203;dklimpel](https://togithub.com/dklimpel) in
[https://github.com/tcort/markdown-link-check/pull/306](https://togithub.com/tcort/markdown-link-check/pull/306)
- chore(deps): bump actions/setup-node from 3 to 4 by
[@&#8203;dependabot](https://togithub.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/300](https://togithub.com/tcort/markdown-link-check/pull/300)
- chore: Upgrade to ESLint 9 by
[@&#8203;nschonni](https://togithub.com/nschonni) in
[https://github.com/tcort/markdown-link-check/pull/318](https://togithub.com/tcort/markdown-link-check/pull/318)
- Check GitHub markdown section links by
[@&#8203;rkitover](https://togithub.com/rkitover) in
[https://github.com/tcort/markdown-link-check/pull/312](https://togithub.com/tcort/markdown-link-check/pull/312)
- docs: add example for GitLab pipeline by
[@&#8203;dklimpel](https://togithub.com/dklimpel) in
[https://github.com/tcort/markdown-link-check/pull/309](https://togithub.com/tcort/markdown-link-check/pull/309)
- ci: Use matrix for cross-OS testing by
[@&#8203;nschonni](https://togithub.com/nschonni) in
[https://github.com/tcort/markdown-link-check/pull/307](https://togithub.com/tcort/markdown-link-check/pull/307)
- chore(deps): bump actions/checkout from 3 to 4 by
[@&#8203;dependabot](https://togithub.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/301](https://togithub.com/tcort/markdown-link-check/pull/301)
- chore(deps): bump commander from 10.0.1 to 12.1.0 by
[@&#8203;dependabot](https://togithub.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/322](https://togithub.com/tcort/markdown-link-check/pull/322)
- chore(deps-dev): bump eslint from 8.57.0 to 9.3.0 by
[@&#8203;dependabot](https://togithub.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/321](https://togithub.com/tcort/markdown-link-check/pull/321)
- chore(deps-dev): bump mocha from 10.3.0 to 10.4.0 by
[@&#8203;dependabot](https://togithub.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/311](https://togithub.com/tcort/markdown-link-check/pull/311)
- chore(deps-dev): bump express from 4.18.3 to 4.19.1 by
[@&#8203;dependabot](https://togithub.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/308](https://togithub.com/tcort/markdown-link-check/pull/308)
- chore(deps-dev): bump express from 4.18.3 to 4.19.2 by
[@&#8203;dependabot](https://togithub.com/dependabot) in
[https://github.com/tcort/markdown-link-check/pull/310](https://togithub.com/tcort/markdown-link-check/pull/310)

#### New Contributors

- [@&#8203;rkitover](https://togithub.com/rkitover) made their first
contribution in
[https://github.com/tcort/markdown-link-check/pull/312](https://togithub.com/tcort/markdown-link-check/pull/312)

**Full Changelog**:
https://github.com/tcort/markdown-link-check/compare/v3.12.1...v3.12.2

###
[`v3.12.1`](https://togithub.com/tcort/markdown-link-check/blob/HEAD/CHANGELOG.md#Version-3121)

[Compare
Source](https://togithub.com/tcort/markdown-link-check/compare/v3.12.0...v3.12.1)

- fix: fix crash
[#&#8203;297](https://togithub.com/tcort/markdown-link-check/issues/297)
[@&#8203;CanadaHonk](https://togithub.com/CanadaHonk)
- Set `ipv4first` for tests in CI
[@&#8203;dklimpel](https://togithub.com/dklimpel)
- Bump node for release
[@&#8203;dklimpel](https://togithub.com/dklimpel)

###
[`v3.12.0`](https://togithub.com/tcort/markdown-link-check/blob/HEAD/CHANGELOG.md#Version-3120)

[Compare
Source](https://togithub.com/tcort/markdown-link-check/compare/v3.11.2...v3.12.0)

- feat: add basic ignore subpaths argument
[@&#8203;CanadaHonk](https://togithub.com/CanadaHonk)
- feat: add global option to replacementPatterns
[@&#8203;CanadaHonk](https://togithub.com/CanadaHonk)
- refactor: replace url.parse with new URL
[@&#8203;CanadaHonk](https://togithub.com/CanadaHonk)
- docs: update readme to be clearer for projectBaseUrl opt
[@&#8203;nwcm](https://togithub.com/nwcm)
- feat: Add support to load md files via proxy
[@&#8203;dklimpel](https://togithub.com/dklimpel)
-   chore: upgrade dependencies

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4yMzguMSIsInVwZGF0ZWRJblZlciI6IjM3LjM2OC4xMCIsInRhcmdldEJyYW5jaCI6Im1haW4ifQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-25 23:04:51 +00:00
Justin Abrahms 9a80acf637
chore: An image for parsing repo spec compliance. (#254)
```
$ docker build -t specfinder .
$ docker run --mount src=/path/tojava-sdk/,target=/appdir,type=bind -it specfinder \
    spec_finder.py --code-directory /appdir --diff --json-report
```

This will generate `java-report.json` in the java-sdk folder which we
can use for other automation.

The report looks like this:

```json
{
    "extra": [
        "1.1.8",
        "2.4.2",
        "2.4.3",
        "2.4.4",
        "2.4.5",
        "4.1.4"
    ],
    "missing": [
        "1.1.2.4",
        "1.7.1",
        "1.7.2.1",
        "1.7.3",
        "1.7.4",
        "1.7.5",
        "1.7.6",
        "1.7.7",
        "1.7.8",
        "2.4.2.1",
        "2.5.2",
        "3.2.2.3",
        "3.2.2.4",
        "3.2.4.2",
        "4.1.4.1",
        "5.1.5",
        "5.3.4.1",
        "5.3.4.2",
        "5.3.4.3",
        "5.3.5"
    ],
    "different-text": [
        "1.1.3",
        "1.1.6",
        "1.2.2",
        "2.4.1",
        "2.6.1",
        "3.2.3",
        "3.2.4.1"
    ],
    "good": [
        "1.1.1",
        "1.1.2.1",
        "1.1.2.2",
        "1.1.2.3",
        "1.1.4",
        "1.1.5",
        "1.1.7",
        "1.2.1",
        "1.3.1.1",
        "1.3.2.1",
        "1.3.3.1",
        "1.3.4",
        "1.4.1.1",
        "1.4.10",
        "1.4.11",
        "1.4.12",
        "1.4.13",
        "1.4.14",
        "1.4.14.1",
        "1.4.2.1",
        "1.4.3",
        "1.4.4.1",
        "1.4.5",
        "1.4.6",
        "1.4.7",
        "1.4.8",
        "1.4.9",
        "1.5.1",
        "1.6.1",
        "2.1.1",
        "2.2.1",
        "2.2.10",
        "2.2.2.1",
        "2.2.3",
        "2.2.4",
        "2.2.5",
        "2.2.6",
        "2.2.7",
        "2.2.8.1",
        "2.2.9",
        "2.3.1",
        "2.3.2",
        "2.3.3",
        "2.5.1",
        "3.1.1",
        "3.1.2",
        "3.1.3",
        "3.1.4",
        "3.2.1.1",
        "3.2.2.1",
        "3.2.2.2",
        "3.3.1.1",
        "3.3.1.2.1",
        "3.3.1.2.2",
        "3.3.1.2.3",
        "3.3.2.1",
        "4.1.1",
        "4.1.2",
        "4.1.3",
        "4.2.1",
        "4.2.2.1",
        "4.2.2.2",
        "4.2.2.3",
        "4.3.1",
        "4.3.2.1",
        "4.3.3.1",
        "4.3.4",
        "4.3.5",
        "4.3.6",
        "4.3.7",
        "4.3.8",
        "4.3.9.1",
        "4.4.1",
        "4.4.2",
        "4.4.3",
        "4.4.4",
        "4.4.5",
        "4.4.6",
        "4.4.7",
        "4.5.1",
        "4.5.2",
        "4.5.3",
        "5.1.1",
        "5.1.2",
        "5.1.3",
        "5.1.4",
        "5.2.1",
        "5.2.2",
        "5.2.3",
        "5.2.4",
        "5.2.5",
        "5.2.6",
        "5.2.7",
        "5.3.1",
        "5.3.2",
        "5.3.3"
    ]
}
```

---------

Signed-off-by: Justin Abrahms <justin@abrah.ms>
2024-05-09 09:25:34 -04:00
Todd Baert 681f38a57b
chore: add glossary entry for client object (#256)
I've been missing this glossary entry for some recent content.

---------

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2024-05-03 09:39:37 -04:00
Michael Beemer 8c4d67b550
Add hard line breaks to GitHub notes (#253)
## This PR

- adds hard new lines to GitHub notes

### Notes

GitHub does some magic behind the scenes that isn't part of the markdown
standard. This causes some issues on our doc site. Explicitly adding a
hard line break using double space helps us avoid this issue.

### Follow-up Tasks

Update the docs to include this change.

---------

Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2024-03-25 10:36:13 -04:00
Todd Baert 5f262bdd12
fix: use jsx syntax for legend color (#251)
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2024-03-11 12:35:50 -04:00
Ryan Lamb 1513e9a021
fix: Include hook hints in evaluation options type. (#250)
<!-- Please use this template for your pull request. -->
<!-- Please use the sections that you need and delete other sections -->

## This PR
In section 4.5.1 of hooks it specifies:
```
> `Flag evaluation options` **MAY** contain `hook hints`, a map of data to be provided to hook invocations.
```

But it does not include this in the types.md file, which makes this a
bit confusing.

### Related Issues
<!-- add here the GitHub issue that this PR resolves if applicable -->


### Notes
<!-- any additional notes for this PR -->

### Follow-up Tasks
<!-- anything that is related to this PR but not done here should be
noted under this section -->
<!-- if there is a need for a new issue, please link it here -->

### How to test
<!-- if applicable, add testing instructions under this section -->

Signed-off-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>
2024-03-11 12:03:38 -04:00
Todd Baert 37cf68b0d6
chore: clarify event/domain scoping, lifecycle (#248)
Adding some additional non-normative text to clarify a few things at the
prompt of @federicobond .

The meaning and justification should be self-evident, and if it's not I
did a bad job. 😅

---------

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2024-03-01 09:10:34 -05:00
Federico Bond 57829b0c4f
fix: add missing Provider Event Details 'error code' field (#249)
Requirement 5.1.5 speaks of a Provider Event Details `error code` field
but this field was missing from the specified type.

This field is required to then populate the Event Details `error code`
field.

Signed-off-by: Federico Bond <federicobond@gmail.com>
2024-02-29 14:33:37 -05:00
Todd Baert 31da456d95
fix: minor corrections around FATAL state (#247)
No substantial changes here, just editorial improvements and
corrections.

When implementing https://github.com/open-feature/spec/pull/241, I
noticed a few oversights (see comments).

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2024-02-22 15:26:10 -05:00
Todd Baert ae286cf519
feat: internal provider state, new client events (#241)
* moves provider state into SDK (SDK now maintains provider
state/lifecycle; provider interface is now "stateless")
* refine semantics around client state when context is
pending/reconciled by using new events/states, instead of STALE
* add PROVIDER_FATAL error code
* add PROVIDER_RECONCILING event (client only)
* add RECONCILING provider status status (client only)

Resolves: https://github.com/open-feature/spec/issues/238
---------

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Co-authored-by: Lukas Reining <lukas.reining@codecentric.de>
Co-authored-by: Kavindu Dodanduwa <Kavindu-Dodan@users.noreply.github.com>
Co-authored-by: Fabrizio Demaria <fabrizio.f.demaria@gmail.com>
2024-02-21 19:58:26 -05:00
Kavindu Dodanduwa a77f18b1ae
feat: introduce OFREP to appendix section (#246)
## This PR

Introduce OpenFeature Remote Evaluation Protocol(OFREP) as an appendix
with minimal details for visibility.

---------

Signed-off-by: Kavindu Dodanduwa <kavindudodanduwa@gmail.com>
Signed-off-by: Kavindu Dodanduwa <Kavindu-Dodan@users.noreply.github.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2024-02-20 13:48:09 -08:00
Max VelDink 2c36abdc54
docs: add language-specific initialize naming (#244)
Signed-off-by: Max VelDink <maxveldink@gmail.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
2024-02-09 22:41:25 +01:00
renovate[bot] b58c3b4ec6
chore(deps): update dependency markdownlint-cli to ^0.39.0 (#240)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-08 12:17:12 -05:00
Lukas Reining 1889aa799a
feat: context propagation (#227)
Signed-off-by: Lukas Reining <lukas.reining@codecentric.de>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Kavindu Dodanduwa <Kavindu-Dodan@users.noreply.github.com>
2024-02-07 15:25:06 +01:00
renovate[bot] dc29976ef1
chore(deps): update dependency prettier to v3.2.5 (#243)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-07 12:25:20 +00:00
Todd Baert ffd555254a
fix: language-neutral verbiage for set-and-wait (#242)
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2024-02-06 15:20:24 -05:00
Ryan Lamb 738bb41a3e
chore: Clarify resolution detail reason. (#239)
Signed-off-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2024-02-05 12:29:50 -05:00
Michael Beemer a0c79d7e00
docs: remove extra backtick character
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2024-01-31 15:22:24 -05:00
Michael Beemer afe1c7ea72
feat: add domain as an openfeature concept (#229)
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2024-01-24 08:49:14 -05:00
renovate[bot] 2ad67fc62a
chore(deps): update dependency prettier to v3.2.4 (#235)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-20 16:59:07 +00:00
renovate[bot] c72b95177c
chore(deps): update dependency prettier to v3.2.3 (#234)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-20 07:32:09 +00:00
renovate[bot] d071994f0a
chore(deps): update dependency prettier to v3.2.2 (#233)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-17 09:14:38 +00:00
Michael Beemer e0ebcbbc22
docs: improve spec readme (#232)
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Co-authored-by: Justin Abrahms <justin@abrah.ms>
2024-01-16 17:29:03 -05:00
renovate[bot] d04fd35d34
chore(deps): update dependency markdownlint-cli to ^0.38.0 (#206)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-16 11:56:15 -05:00
renovate[bot] 1b25be11cd
chore(deps): update dependency prettier to v3.2.1 (#230)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-16 16:35:25 +00:00
Michael Beemer db20fee6b9
chore: improve Renovate config (#231)
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2024-01-16 08:56:35 -05:00
renovate[bot] bfdc8d1940
chore(deps): update actions/checkout action to v4 (#207)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2024-01-10 11:09:52 -05:00
renovate[bot] 233094b0f3
chore(deps): update actions/setup-node action to v4 (#211)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 11:06:13 -05:00
renovate[bot] c21077b43b
chore(deps): update dependency prettier to v3.1.1 (#224)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-09 15:11:04 -05:00
Todd Baert b1edf2a385
chore: add repo stats (#226)
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2024-01-07 22:55:36 -05:00
renovate[bot] 1bb141b1df
chore(deps): update actions/setup-python action to v5 (#223)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-18 08:34:53 -05:00
Todd Baert c75eb75523
chore: move 1.1.8 content to 1.1.2.4 (#225)
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2023-12-15 10:28:23 -05:00
Bill Lynch 262da8f7cf
feat: emit events on context change (client-only) (#200)
Signed-off-by: BillLynch <llynch.bill@gmail.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Kavindu Dodanduwa <Kavindu-Dodan@users.noreply.github.com>
2023-12-13 16:34:01 -05:00
Michael Beemer 5ed6c0cc08
define how context can be managed for named provider using static context paradigm (#221)
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2023-12-08 13:02:20 -05:00
Michael Beemer c4b66e4f39
fix: invalid links detected by Docusaurus (#222)
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2023-12-05 12:51:37 -05:00
Todd Baert 73922b2efb
feat: NOT_READY after provider shutdown (#216)
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2023-11-28 12:14:19 -05:00
Todd Baert 2973c53f0f
fix: add dynamic-context condition to 4.1.4 (#217)
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2023-11-21 09:29:44 -05:00
renovate[bot] 816601734a
chore(deps): update dependency prettier to v3.1.0 (#204)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-13 14:32:36 -05:00
Todd Baert 34e0a33abb
fix: correct `event details` type, include `provider name` (#210)
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2023-11-13 12:40:44 -05:00
Federico Bond 814e332c6e
fix: add stale state to provider state diagram (#212)
Signed-off-by: Federico Bond <federicobond@gmail.com>
2023-10-25 12:46:24 -04:00
Todd Baert d0a87da15f
chore: clarify eval details funcs/structs (#209)
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2023-09-26 09:19:11 -04:00
Todd Baert e2eb2b48c1
fix: broken link, outdated link (#208)
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2023-09-12 10:54:24 -04:00
Todd Baert a0b3777909
chore: fix provider given clause
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2023-08-29 14:50:39 -04:00
Todd Baert 230bad2f99
feat: add appendix b, gherkin suite (#203)
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Kavindu Dodanduwa <Kavindu-Dodan@users.noreply.github.com>
Co-authored-by: Liran M <77168114+liran2000@users.noreply.github.com>
2023-08-29 11:39:46 -04:00
Michael Beemer f45bb276e1
chore: fix frontmatter title issue and typos
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2023-08-22 09:11:18 -04:00
renovate[bot] cd42c52bd1
chore(deps): update dependency prettier to v3 (#198)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-21 11:44:28 -04:00
Thomas Poignant 4b21642564
feat: blocking setProvider (#201)
Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
2023-08-15 16:39:09 +00:00
Todd Baert 8e8c344796
feat: STALE state, run handlers for current state immediately, provider name (#196)
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Justin Abrahms <justin@abrah.ms>
2023-08-15 10:53:01 -04:00
Kavindu Dodanduwa 2a2539d2ba
feat: introduce appendix section (#199)
Signed-off-by: Kavindu Dodanduwa <kavindudodanduwa@gmail.com>
Signed-off-by: Kavindu Dodanduwa <Kavindu-Dodan@users.noreply.github.com>
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Federico Bond <federicobond@gmail.com>
2023-08-01 16:27:56 -04:00
Todd Baert 007502bb37
feat!: add static/dynamic context (#171)
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Skye Gill <gill.skye95@gmail.com>
Co-authored-by: Justin Abrahms <jabrahms@ebay.com>
Co-authored-by: Pete Hodgson <github@thepete.net>
Co-authored-by: Jonathan Norris <jonathan@taplytics.com>
Co-authored-by: Kavindu Dodanduwa <Kavindu-Dodan@users.noreply.github.com>
2023-07-21 12:54:12 -04:00
renovate[bot] 2c9344c443
chore(deps): update dependency markdownlint-cli to ^0.35.0 (#194)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-26 11:45:27 -04:00
Todd Baert 77e0b9c69a
fix: various clarifications (#193)
* loosen shutdown requirements to support things like go ctx
* clarify how to handle init/shutdown of providers bound to multiple names
* clarify that authors can await the READY event during init

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2023-06-02 15:38:55 -04:00
Todd Baert 975a908d8d
chore: add events link to readme.
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2023-05-19 12:26:55 -04:00
Todd Baert 9b86728f57
fix: broken anchor links (#188)
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
2023-05-18 12:41:07 -04:00
Todd Baert bf9e45b1d5
feat: add events (#182)
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Justin Abrahms <justin@abrah.ms>
Co-authored-by: Lukas Reining <lukas.reining@codecentric.de>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Co-authored-by: Kavindu Dodanduwa <kavindudodanduwa@gmail.com>
Co-authored-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Tom Carrio <tom@carrio.dev>
Co-authored-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>
Co-authored-by: Ben Rometsch <benrometsch@gmail.com>
Co-authored-by: Pete Hodgson <git@thepete.net>
2023-05-15 09:02:39 -04:00
Todd Baert a4ffec3d44
feat: initialization and shutdown (#179)
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Weyert de Boer <weyert@gmail.com>
Co-authored-by: Justin Abrahms <justin@abrah.ms>
Co-authored-by: Lukas Reining <lukas.reining@codecentric.de>
Co-authored-by: Jonathan Norris <jonathan@taplytics.com>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Co-authored-by: Kavindu Dodanduwa <kavindudodanduwa@gmail.com>
Co-authored-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>
2023-05-08 16:13:36 -04:00
Justin Abrahms b50883c294
add client mapping info to glossary (#186)
Signed-off-by: Justin Abrahms <justin@abrah.ms>
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Co-authored-by: Todd Baert <toddbaert@gmail.com>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2023-05-04 07:55:48 -04:00
Justin Abrahms 4cf8229d23
Spec change for named client -> provider mappings (#183)
Signed-off-by: Justin Abrahms <justin@abrah.ms>
2023-05-02 21:40:54 -07:00
renovate[bot] c74f409c28
chore(deps): update dependency markdown-link-check to v3.11.2 (#185)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-01 09:14:24 -04:00
renovate[bot] a4ff004b2b
chore(deps): update dependency markdownlint-cli to ^0.34.0 (#184)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-01 09:12:40 -04:00
renovate[bot] 00542fc5d4
chore(deps): update dependency prettier to v2.8.8 (#181)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-25 12:52:40 -04:00
renovate[bot] ea451a9964
chore(deps): update dependency markdown-link-check to v3.11.1 (#177)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-25 12:49:11 -04:00
renovate[bot] 2108f87e4c
chore(deps): update dependency prettier to v2.8.7 (#178)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-05 08:27:50 -04:00
Pete Hodgson 74c373e089
OFEP-007: Surfacing flag metadata (#169)
Signed-off-by: Pete Hodgson <github@thepete.net>
2023-01-23 14:57:03 -05:00
renovate[bot] e42dafc8ca
chore(deps): update dependency markdownlint-cli to ^0.33.0 (#170) 2023-01-16 14:39:25 -05:00
Todd Baert 7ed7442c40
fix: links and link checks (#172)
Signed-off-by: Todd Baert <toddbaert@gmail.com>
2023-01-12 12:04:32 -05:00
renovate[bot] 8ebe27b11c
chore(deps): update actions/setup-python action to v4 (#157)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/setup-python](https://togithub.com/actions/setup-python) |
action | major | `v3` -> `v4` |

---

### Release Notes

<details>
<summary>actions/setup-python</summary>

### [`v4`](https://togithub.com/actions/setup-python/compare/v3...v4)

[Compare
Source](https://togithub.com/actions/setup-python/compare/v3...v4)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://app.renovatebot.com/dashboard#github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC4xOS4wIiwidXBkYXRlZEluVmVyIjoiMzQuNjIuMSJ9-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-05 12:19:04 -05:00
Skye Gill 928d513667
docs: include STATIC and CACHED in provider reasons (#166)
Signed-off-by: Skye Gill <gill.skye95@gmail.com>
2022-12-19 16:22:55 -05:00
Nikita Bishonen 136c26af80
Fix usage of wrong type instead of resolution details (#161)
Evaluation details and flag evaluation names usages for resolution
details type were fixed.

Signed-off-by: Nikita Bishonen <git@bshn.rs>
2022-12-02 10:06:30 -05:00
Todd Baert 5270ae8770
chore: clarify timezone requirement (#150)
A small change to make the timezone info on the `Datetime` type
optional. Some SDKs already do not include timezone info.

Since it's loosening this definition, this is non-breaking.

Signed-off-by: Todd Baert <toddbaert@gmail.com>
Co-authored-by: Steve Arch <sarch@cloudbees.com>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2022-11-30 15:08:57 -05:00
renovate[bot] 7f0fdd8e84
chore(deps): update dependency markdownlint-cli to ^0.32.0 (#155)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
|
[markdownlint-cli](https://togithub.com/igorshubovych/markdownlint-cli)
| [`^0.31.1` ->
`^0.32.0`](https://renovatebot.com/diffs/npm/markdownlint-cli/0.31.1/0.32.2)
|
[![age](https://badges.renovateapi.com/packages/npm/markdownlint-cli/0.32.2/age-slim)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://badges.renovateapi.com/packages/npm/markdownlint-cli/0.32.2/adoption-slim)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://badges.renovateapi.com/packages/npm/markdownlint-cli/0.32.2/compatibility-slim/0.31.1)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://badges.renovateapi.com/packages/npm/markdownlint-cli/0.32.2/confidence-slim/0.31.1)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>igorshubovych/markdownlint-cli</summary>

###
[`v0.32.2`](https://togithub.com/igorshubovych/markdownlint-cli/releases/tag/v0.32.2):
0.32.2

[Compare
Source](https://togithub.com/igorshubovych/markdownlint-cli/compare/v0.32.1...v0.32.2)

-   Update `markdownlint` dependency to `0.26.2`
    -   Improve `MD037`/`MD051`/`MD053`
-   Update all dependencies via `Dependabot`

###
[`v0.32.1`](https://togithub.com/igorshubovych/markdownlint-cli/releases/tag/v0.32.1):
0.32.1

[Compare
Source](https://togithub.com/igorshubovych/markdownlint-cli/compare/v0.32.0...v0.32.1)

-   Update `markdownlint` dependency to `0.26.1`
    -   Improve `MD051`
-   Update all dependencies via `Dependabot`

###
[`v0.32.0`](https://togithub.com/igorshubovych/markdownlint-cli/releases/tag/v0.32.0):
0.32.0

[Compare
Source](https://togithub.com/igorshubovych/markdownlint-cli/compare/v0.31.1...v0.32.0)

-   Update `markdownlint` dependency to `0.26.0`
    -   Add `MD051`/`link-fragments`: Link fragments should be valid
- Add `MD052`/`reference-links-images`: Reference links and images
should use a label that is defined
- Add `MD053`/`link-image-reference-definitions`: Link and image
reference definitions should be needed (auto-fixable)
- Improve
`MD010`/`MD031`/`MD035`/`MD039`/`MD042`/`MD044`/`MD049`/`MD050`
    -   Add `markdownlint-disable-line` inline comment
    -   Support `~` paths in configuration files
    -   Improve performance
-   Add `.markdownlint.jsonc` to list of supported configuration files
-   Remove support for end-of-life Node version 12
-   Update all dependencies via `Dependabot`

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://app.renovatebot.com/dashboard#github/open-feature/spec).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzMi4yNDEuMTEiLCJ1cGRhdGVkSW5WZXIiOiIzNC4xNy4xIn0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-28 08:48:17 -05:00
Meg McRoberts c49b306035
Rephrase sentence about evaluation tools (#162)
Signed-off-by: Meg McRoberts <meg.mcroberts@dynatrace.com>
2022-11-24 08:06:20 -05:00
David Hirsch 0f1561ddf4
docs: small changes/corrections (#159)
Signed-off-by: David Hirsch <david.hirsch@dynatrace.com>

## This PR
small corrections to docs

Signed-off-by: David Hirsch <79513080+DavidPHirsch@users.noreply.github.com>
2022-11-09 15:25:52 -05:00
Oleg Nenashev bb3c21a189
Remove @oleg-nenashev from CODEOWNERS (#160)
I am not doing the reviews anymore

Signed-off-by: Oleg Nenashev <o.v.nenashev@gmail.com>
2022-11-09 15:23:21 -05:00
renovate[bot] 3445d95777
Configure Renovate (#151)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

Welcome to [Renovate](https://togithub.com/renovatebot/renovate)! This
is an onboarding PR to help you understand and configure settings before
regular Pull Requests begin.

🚦 To activate Renovate, merge this Pull Request. To disable Renovate,
simply close this Pull Request unmerged.



---
### Detected Package Files

 * `.github/workflows/pr-checks.yaml` (github-actions)
 * `package.json` (npm)

### Configuration Summary

Based on the default config's presets, Renovate will:

  - Start dependency updates only once this onboarding PR is merged
  - Enable Renovate Dependency Dashboard creation.
- If Renovate detects semantic commits, it will use semantic commit type
`fix` for dependencies and `chore` for all others.
- Ignore `node_modules`, `bower_components`, `vendor` and various
test/tests directories.
  - Autodetect whether to pin dependencies or maintain ranges.
  - Rate limit PR creation to a maximum of two per hour.
  - Limit to maximum 10 open PRs at any time.
  - Group known monorepo packages together.
  - Use curated list of recommended non-monorepo package groupings.
  - A collection of workarounds for known problems with packages.

🔡 Would you like to change the way Renovate is upgrading your
dependencies? Simply edit the `renovate.json` in this branch with your
custom config and the list of Pull Requests in the "What to Expect"
section below will be updated the next time Renovate runs.

---

### What to Expect

With your current configuration, Renovate will create 5 Pull Requests:

<details>
<summary>chore(deps): pin dependencies</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/pin-dependencies`
  - Merge into: `main`
- Pin
[markdown-link-check](https://togithub.com/tcort/markdown-link-check) to
`3.10.2`
- Pin [markdown-toc](https://togithub.com/jonschlinkert/markdown-toc) to
`1.2.0`
- Pin
[markdownlint-cli](https://togithub.com/igorshubovych/markdownlint-cli)
to `0.31.1`
  - Pin [prettier](https://togithub.com/prettier/prettier) to `2.6.2`


</details>

<details>
<summary>chore(deps): update dependency markdown-link-check to
v3.10.3</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/markdown-link-check-3.x`
  - Merge into: `main`
- Upgrade
[markdown-link-check](https://togithub.com/tcort/markdown-link-check) to
`3.10.3`


</details>

<details>
<summary>chore(deps): update dependency markdownlint-cli to
v0.32.2</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/markdownlint-cli-0.x`
  - Merge into: `main`
- Upgrade
[markdownlint-cli](https://togithub.com/igorshubovych/markdownlint-cli)
to `0.32.2`


</details>

<details>
<summary>chore(deps): update dependency prettier to v2.7.1</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/prettier-2.x`
  - Merge into: `main`
- Upgrade [prettier](https://togithub.com/prettier/prettier) to `2.7.1`


</details>

<details>
<summary>chore(deps): update actions/setup-python action to v4</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/actions-setup-python-4.x`
  - Merge into: `main`
- Upgrade
[actions/setup-python](https://togithub.com/actions/setup-python) to
`v4`


</details>

<br />

🚸 Branch creation will be limited to maximum 2 per hour, so it doesn't
swamp any CI resources or spam the project. See docs for `prhourlylimit`
for details.


---

 Got questions? Check out Renovate's
[Docs](https://docs.renovatebot.com/), particularly the Getting Started
section.
If you need any further assistance then you can also [request help
here](https://togithub.com/renovatebot/renovate/discussions).


---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://app.renovatebot.com/dashboard#github/open-feature/spec).

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-24 17:17:46 -04:00
Todd Baert c1792369e3
chore: section clarity, harden API, providers (#149)
**Marking Evaluation API and Providers `hardening`** and a few
non-functional changes here that improve structure.

specifically:

- explicitly mark and number all sections
- section headings have consistent numbering (provider section numbering
was different than the other docs)
- removed "draft" language 

An alternative to marking all of `provider` and `evaluation API` as
hardening would be just marking all existing sections therein as
hardening, which might be better since we'd have to do that anyway if we
added a new experimental section.

see: https://github.com/open-feature/spec/issues/146 for more on release
goals.

Signed-off-by: Todd Baert <toddbaert@gmail.com>
2022-10-17 11:00:13 -04:00
Todd Baert 18d2f02d89
Merge pull request #148 from open-feature/doc-statuses
chore: add status badges
2022-10-13 08:47:28 -04:00
Todd Baert a5b7b280e7
chore: add status badges
Signed-off-by: Todd Baert <toddbaert@gmail.com>
2022-10-13 08:34:57 -04:00
Todd Baert 73430ed040
Merge pull request #139 from open-feature/doc-statuses
chore: add additional document statuses
2022-10-12 14:30:40 -04:00
Todd Baert ed9a4bc01b
remove mixed
Signed-off-by: Todd Baert <toddbaert@gmail.com>
2022-10-12 13:59:09 -04:00
Todd Baert 29d6378ee7
lint errors
Signed-off-by: Todd Baert <toddbaert@gmail.com>
2022-10-05 13:31:55 -04:00
Todd Baert 73e1ed6656
more description to stable
Signed-off-by: Todd Baert <toddbaert@gmail.com>
2022-10-05 13:26:22 -04:00
Todd Baert 3b800fd9b0
add more explanation, TSC approval
Signed-off-by: Todd Baert <toddbaert@gmail.com>
2022-10-05 11:28:03 -04:00
Todd Baert e2629c1873
add additional document statuses
Co-authored-by: Daniel Dyla <dyladan@users.noreply.github.com>
Signed-off-by: Todd Baert <toddbaert@gmail.com>
2022-09-27 14:50:16 -04:00
Ryan Lamb f74a8d9876
feat!: make error code an enum and include optional error message (#142)
See https://github.com/open-feature/spec/issues/141 for context.

Signed-off-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>
Co-authored-by: Todd Baert <toddbaert@gmail.com>
2022-09-27 11:39:05 -04:00
Todd Baert e6434df8b2
Merge pull request #140 from open-feature/reasons
add reasons, table
2022-09-23 13:12:49 -04:00
Todd Baert 9f6563e457
add reasons, table
Signed-off-by: Todd Baert <toddbaert@gmail.com>
2022-09-23 11:02:35 -04:00
Michael Beemer ddd5539c0e
Remove private keyword from provider hook example (#137)
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2022-09-03 16:51:45 -04:00
James Milligan 3fcb85678d
removed evaluation options from provider method signatures (#134)
BREAKING CHANGE

Resolves: #133 

Signed-off-by: James-Milligan <james@omnant.co.uk>
2022-08-25 10:20:43 -04:00
Michael Beemer 186741ebc2
Consolidate evaluation context merge order in the spec (#132)
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
2022-08-15 13:29:53 -04:00
Todd Baert 3da57e489e
Merge pull request #129 from toddbaert/feat/remove-xformation
Remove context transformation, ref in provider hooks
2022-08-10 09:39:43 -04:00
Todd Baert 86cd353eef
Update specification/sections/02-providers.md
Co-authored-by: Justin Abrahms <justin@abrah.ms>
Signed-off-by: Todd Baert <toddbaert@gmail.com>
2022-08-09 11:09:07 -04:00
Todd Baert 901dd3a46d Remove context transformation, ref in provider hooks
Signed-off-by: Todd Baert <toddbaert@gmail.com>
2022-08-09 10:58:05 -04:00
Michael Beemer b3a0cc6e89
update fractional evaluation glossary definition (#127)
Signed-off-by: Michael Beemer <michael.beemer@dynatrace.com>
2022-08-09 09:38:17 -04:00
Justin Abrahms c5b7b28d73
Document context merging order for before hook ctx (#125)
Signed-off-by: Justin Abrahms <jabrahms@ebay.com>
Co-authored-by: Todd Baert <toddbaert@gmail.com>
2022-08-08 21:29:50 -04:00
Justin Abrahms 0b272d5822
Merge pull request #119 from open-feature/provider-hooks
Remove context transformers. Add provider hooks
2022-08-05 10:52:58 -07:00
Justin Abrahms 9bc54eb37f
Update specification/sections/02-providers.md
Co-authored-by: Todd Baert <toddbaert@gmail.com>
Signed-off-by: Justin Abrahms <jabrahms@ebay.com>
2022-08-05 10:50:14 -07:00
Justin Abrahms 946be25b3c
Update based on review comments.
Provider interface must add hook mechanism.
Make wording clearer.
Ensure Provider is listed alongside other hook registrars.

Signed-off-by: Justin Abrahms <jabrahms@ebay.com>
2022-08-04 08:47:29 -07:00
Justin Abrahms 678e49280f
Remove context transformers. Add provider hooks
Signed-off-by: Justin Abrahms <jabrahms@ebay.com>
2022-08-03 14:21:49 -07:00
Thomas Poignant 607e74b5cf
EvaluationContext each key should be unique (#121)
Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
2022-08-03 15:40:17 -04:00
Robert Grassian a97fe2c3af
evaluation context requirement to allow fetching all custom fields (#120)
Signed-off-by: Robert Grassian <robert.grassian@split.io>
Co-authored-by: Justin Abrahms <justin@abrah.ms>
2022-08-03 15:29:21 -04:00
Todd Baert 8c24a8d900
Merging context (#117)
* Merging context

Signed-off-by: Todd Baert <toddbaert@gmail.com>

* Update specification/evaluation-context.md

Co-authored-by: Skye Gill <gill.skye95@gmail.com>
Signed-off-by: Skye Gill <gill.skye95@gmail.com>
Signed-off-by: Todd Baert <toddbaert@gmail.com>

Co-authored-by: Skye Gill <gill.skye95@gmail.com>
2022-08-01 08:33:59 -04:00
Michael Beemer c18fb0eb75
restructure spec and include metadata for docs (#118)
Signed-off-by: Michael Beemer <michael.beemer@dynatrace.com>
2022-07-29 09:50:22 -04:00
Justin Abrahms 7d4648cb96
Merge pull request #116 from thomaspoignant/evaluation-proposition
Have different methods for float and int
2022-07-26 09:06:03 -07:00
Thomas Poignant 505c26386f mention only numbers
Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
2022-07-26 18:04:59 +02:00
Thomas Poignant 4ee9082d31 fix after Todd review
Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
2022-07-25 14:30:34 +02:00
Thomas Poignant f1ca6b9a23 Specify all the types
Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
2022-07-25 11:26:17 +02:00
Thomas Poignant 2869a85bc2 linting issue
Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
2022-07-24 10:58:16 +02:00
Thomas Poignant 057a80fc19 Fix review comments
Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
2022-07-24 10:56:55 +02:00
Thomas Poignant 9512910b7c Rebuild json file
Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
2022-07-23 17:33:25 +02:00
Thomas Poignant c6a4f3a53a spec flag evaluation float int types
Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
2022-07-23 17:28:50 +02:00
48 changed files with 7129 additions and 2382 deletions

1
.github/CODEOWNERS vendored
View File

@ -1 +0,0 @@
* @oleg-nenashev @toddbaert

View File

@ -14,8 +14,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- name: Generate JSON files
run: make parse
@ -34,8 +34,8 @@ jobs:
json-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- name: Lint
run: make lint
@ -44,11 +44,11 @@ jobs:
markdown-toc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/setup-node@v4
with:
node-version: "18"
node-version: "22"
- name: Generate ToC
run: make markdown-toc

38
.github/workflows/pr-python.yaml vendored Normal file
View File

@ -0,0 +1,38 @@
name: PR Python checks
on:
pull_request:
branches:
- main
permissions:
contents: read
env:
WORKING_DIR: tools/repo_parser
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.13
- name: Install dependencies
working-directory: ${{ env.WORKING_DIR }}
run: pip install -r requirements.txt
- name: Format
working-directory: ${{ env.WORKING_DIR }}
run: ruff format --check
- name: Lint
working-directory: ${{ env.WORKING_DIR }}
run: ruff check
- name: Typing
working-directory: ${{ env.WORKING_DIR }}
run: mypy
- name: Unit tests
working-directory: ${{ env.WORKING_DIR }}
run: pytest

21
.github/workflows/repo-stats.yaml vendored Normal file
View File

@ -0,0 +1,21 @@
# Uses https://github.com/jgehrcke/github-repo-stats to overcome the 14-day limitation of GitHub's built-in traffic statistics.
name: "Repo Stats"
on:
schedule:
# Run this once per day, towards the end of the day for keeping the most
# recent data point most meaningful (hours are interpreted in UTC).
- cron: "0 23 * * *"
workflow_dispatch: # Allow for running this manually.
jobs:
snapshot:
name: github-repo-stats
runs-on: ubuntu-latest
steps:
- name: run-ghrs
# Use latest release.
uses: jgehrcke/github-repo-stats@v1.4.2
with:
databranch: github-repo-stats
ghtoken: ${{ secrets.REPO_STATS_TOKEN }}

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
node_modules
__pycache__
.idea

View File

@ -6,3 +6,7 @@ MD033:
- details
MD024:
siblings_only: true
MD025:
front_matter_title: ""
MD028: false # no blank lines in block quote - disabling b/c it fires on 2 consecutive block quotes :/

1
CODEOWNERS Normal file
View File

@ -0,0 +1 @@
/specification/ @open-feature/technical-steering-committee

View File

@ -1,16 +1,13 @@
IS_PYTHON_INSTALLED = $(shell which python >> /dev/null 2>&1; echo $$?)
ALL_DOCS := $(shell find . -type f -name '*.md' -not -path './.github/*' -not -path './node_modules/*' | sort)
parse: clean _check_python
parse: _check_python
@python ./tools/specification_parser/specification_parser.py
clean:
@find ./specification -name '*.json' -delete
lint: node_modules
@python ./tools/specification_parser/lint_json_output.py specification.json
./node_modules/.bin/markdownlint --ignore node_modules/ --ignore tools/ **/*.md
./node_modules/.bin/markdown-link-check -c .markdown-link-check-config.json README.md specification/*.md
./node_modules/.bin/markdown-link-check -c .markdown-link-check-config.json README.md specification/*.md specification/**/*.md
fix: node_modules
prettier -w **/*.md
@ -25,6 +22,7 @@ _check_python:
&& echo "" \
&& exit 1; \
fi;
.PHONY: markdown-toc
markdown-toc: node_modules
@if ! npm ls markdown-toc; then npm ci; fi
@ -32,6 +30,7 @@ markdown-toc: node_modules
if grep -q '<!-- tocstop -->' $$f; then \
echo markdown-toc: processing $$f; \
npx --no -- markdown-toc --bullets="-" --no-first-h1 --no-stripHeadingTags -i $$f || exit 1; \
npx --no -- prettier -w $$f; \
else \
echo markdown-toc: no TOC markers, skipping $$f; \
fi; \

View File

@ -1,29 +1,55 @@
# OpenFeature Specification (Draft)
<!-- markdownlint-disable MD033 -->
<!-- x-hide-in-docs-start -->
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/open-feature/community/0e23508c163a6a1ac8c0ced3e4bd78faafe627c7/assets/logo/horizontal/white/openfeature-horizontal-white.svg" />
<img align="center" alt="OpenFeature Logo" src="https://raw.githubusercontent.com/open-feature/community/0e23508c163a6a1ac8c0ced3e4bd78faafe627c7/assets/logo/horizontal/black/openfeature-horizontal-black.svg" />
</picture>
</p>
[![Roadmap](https://img.shields.io/static/v1?label=Roadmap&message=public&color=green)](https://github.com/orgs/open-feature/projects/1) [![Contributing](https://img.shields.io/static/v1?label=Contributing&message=guide&color=blue)](https://github.com/open-feature/.github/blob/main/CONTRIBUTING.md) [![Code of Conduct](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](https://github.com/open-feature/.github/blob/main/CODE_OF_CONDUCT.md)
<h2 align="center">OpenFeature Specification</h2>
<!-- x-hide-in-docs-end -->
<!-- The 'github-badges' class is used in the docs -->
<p align="center" class="github-badges">
<a href="https://github.com/orgs/open-feature/projects/1">
<img alt="Roadmap" src="https://img.shields.io/static/v1?label=Roadmap&message=public&color=green" />
</a>
<a href="https://github.com/open-feature/.github/blob/main/CONTRIBUTING.md">
<img alt="Contributing" src="https://img.shields.io/static/v1?label=Contributing&message=guide&color=blue" />
</a>
<a href="https://cloud-native.slack.com/archives/C0344AANLA1">
<img alt="Slack" src="https://img.shields.io/badge/slack-%40cncf%2Fopenfeature-brightgreen?style=flat&logo=slack"/>
</a>
<a href="https://bestpractices.coreinfrastructure.org/projects/6601">
<img alt="CII Best Practices" src="https://bestpractices.coreinfrastructure.org/projects/6601/badge" />
</a>
</p>
<!-- x-hide-in-docs-start -->
[OpenFeature](https://openfeature.dev) is an open specification that provides a vendor-agnostic, community-driven API for feature flagging that works with your favorite feature flag management tool or in-house solution.
This repository describes the requirements and expectations for OpenFeature.
> :warning: Ongoing research can be found in the [research repo](https://github.com/open-feature/research). For definitions of key terminology, see the [glossary](./specification/glossary.md).
## Design Principles
- Compatibility with existing feature flag offerings
- Simple, understandable APIs
- Vendor agnosticism
- Language agnosticism
- Low/no dependency
- Extensibility
The OpenFeature specification must be designed with:
- compatibility with existing feature flag offerings.
- simple, understandable APIs.
- vendor agnosticism.
- language agnosticism.
- low/no dependency.
- extensibility.
### SDKs and Client Libraries
The project aims to provide a unified API and SDK for feature flag management in various technology stacks. The flag evaluation logic will **not** be handled in the OpenFeature SDK itself but provide a mechanism for interfacing with an external evaluation engine in a vendor agnostic way.
The project aims to provide a unified API and SDK feature flag evaluation across popular technology stacks.
The OpenFeature SDK provides a mechanism for interfacing
with an external evaluation engine in a vendor agnostic way;
it does **not** itself handle the flag evaluation logic.
The OpenFeature project will include client libraries for common technology stacks including, but not limited to:
- Golang
- Java
- JavaScript/TypeScript (Node.js)
An up-to-date SDK compatibility overview can be found [here](https://openfeature.dev/docs/reference/technologies/sdk-compatibility).
### Tooling

3419
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
"devDependencies": {
"markdown-link-check": "^3.10.2",
"markdown-toc": "^1.2.0",
"markdownlint-cli": "^0.31.1",
"prettier": "^2.6.2"
"markdownlint-cli": "^0.44.0",
"prettier": "^3.0.0"
}
}

13
renovate.json Normal file
View File

@ -0,0 +1,13 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":automergeTypes",
":automergeStableNonMajor",
"npm:unpublishSafe"
],
"semanticCommits": "enabled",
"labels": [
"renovate"
]
}

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,26 @@
---
id: intro
title: Introduction
description: An introduction to the OpenFeature specification.
sidebar_position: 0
---
# OpenFeature Specification
## Contents
- [Glossary](./glossary.md)
- [Types](./types.md)
- [Evaluation API](./flag-evaluation.md)
- [Providers](./providers.md)
- [Evaluation Context](./evaluation-context.md)
- [Hooks](./hooks.md)
- [Evaluation API](./sections/01-flag-evaluation.md)
- [Providers](./sections//02-providers.md)
- [Evaluation Context](./sections/03-evaluation-context.md)
- [Hooks](./sections/04-hooks.md)
- [Events](./sections/05-events.md)
- [Tracking](./sections/06-tracking.md)
- [Appendix A: Included Utilities](./appendix-a-included-utilities.md)
- [Appendix B: Gherkin Suites](./appendix-b-gherkin-suites.md)
- [Appendix C: OFREP](./appendix-c-ofrep.md)
- [Appendix D: Observability](./appendix-d-observability.md)
## Conformance
@ -28,7 +41,42 @@ An implementation is not compliant if it fails to satisfy one or more of the "MU
## Document Statuses
| Status | Explanation |
| -------------------- | ----------------------------- |
| No Explicit "Status" | Equivalent to Experimental. |
| Experimental | Breaking changes are allowed. |
Sections and subsections within the specification are marked with statuses indicating their stability level.
Functionality described in the specification graduates through these statuses with increasing stability.
Stability levels apply only to normative sections within the specification; editorial changes to examples and explanations are exempt from these constraints.
It is the responsibility of the [Technical Steering Committee](https://github.com/open-feature/community/blob/main/governance-charter.md#tsc-members) to consider and approve the graduation of documents.
Possible statuses are described below:
### Experimental
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
Specification sections that are marked as `Experimental` contain functionality under active development. Breaking changes are allowed and may be made without deprecation notices or warnings with minor version updates. We recommend you use these features in experimental environments and not in production.
Put simply:
> We're testing these features out. Things could change anytime.
### Hardening
[![hardening](https://img.shields.io/static/v1?label=Status&message=hardening&color=yellow)](https://github.com/open-feature/spec/tree/main/specification#hardening)
Sections marked as `Hardening` describe functionality with an emphasis on stabilizing existing requirements. Breaking changes require consensus by the [Technical Steering Committee](https://github.com/open-feature/community/blob/main/governance-charter.md#tsc-members) but may still be made with minor version updates. These features are suitable for use in production environments. Feedback is encouraged.
Put simply:
> We believe these features are ready for production use, and hope for feedback.
### Stable
[![stable](https://img.shields.io/static/v1?label=Status&message=stable&color=green)](https://github.com/open-feature/spec/tree/main/specification#stable)
Sections marked as `Stable` do not allow breaking changes without a major version update. They can be used in production with a high degree of confidence.
Put simply:
> These features are stable and battle-hardened.
> [!NOTE]
> No explicit status = `Experimental`

View File

@ -0,0 +1,435 @@
---
id: appendix-a
title: "Appendix A: Included Utilities"
description: Information on OpenFeature ecosystem utilities
sidebar_position: 4
---
# Appendix A: Included Utilities
This document contains requirements for auxiliary utilities provided by the SDK, such as testing utilities.
## In-memory provider
> OpenFeature SDK implementations **SHOULD** provide an `in-memory provider`.
The in-memory provider is intended to be used for testing; SDK consumers may use it for their use cases.
Hence, the packaging, naming, and access modifiers must be set appropriately.
This provider **MUST** have the following features:
- The provider is initiated with a pre-defined `flag set` provided in the constructor.
- The flag structure must be minimalist and should help test the OpenFeature specification.
- EvaluationContext support should be provided through callbacks/lambda expressions.
- The provider must support a means of updating the `flag set`, resulting in the emission of `PROVIDER_CONFIGURATION_CHANGED` events.
- The change event should consider all flags changed; a union of all previous and all new flag keys should be supplied in the `flags changed` field.
- The provider must be maintained to support specification changes.
## SDK end-to-end testing
> E2E tests must utilize [in-memory provider](#in-memory-provider) defined within the SDK and must be self-contained.
OpenFeature project maintains an end-to-end(e2e) test suite defined with [Gherkin syntax](https://cucumber.io/docs/gherkin/).
These test definitions reside in [Appendix B](./appendix-b-gherkin-suites.md)
```mermaid
flowchart LR
subgraph SDK
A[e2e Tests] -.-> B[In-memory provider]
end
```
## Multi-Provider
### Introduction
The OpenFeature Multi-Provider wraps multiple underlying providers in a unified interface, allowing the SDK client to transparently interact with all those providers at once.
This allows use cases where a single client and evaluation interface is desired, but where the flag data should come from more than one source, or where tracking events should be sent to multiple providers simultaneously.
Some examples:
- **Migration**: When migrating between two providers, you can run both in parallel under a unified flagging interface. As flags are added to the new provider, the Multi-Provider will fetch flags from the new provider first, falling back to the old provider.
- **Multiple Data Sources**: The Multi-Provider allows you to combine many sources of flagging data, such as environment variables, local files, database values and SaaS hosted feature management systems.
- **Analytics Aggregation**: Send tracking events to multiple analytics providers simultaneously.
Check the [OpenFeature JavaScript Multi-Provider](https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/multi-provider) for a reference implementation.
### Basics
The provider is initialized by passing a list of provider instances it should evaluate.
The order of the array defines the order in which sources should be evaluated.
The provider whose value is ultimately used will depend on the "strategy" that is provided, which can be chosen from a set of pre-defined ones or implemented as custom logic.
For example:
```typescript
const multiProvider = new MultiProvider(
[
{
provider: new ProviderA(),
},
{
provider: new ProviderB()
}
],
new FirstMatchStrategy()
)
await OpenFeature.setProviderAndWait(multiProvider)
```
From the perspective of the SDK client, this provider will now act as a "normal" spec-compliant provider, while handling the complexities of aggregating multiple providers internally.
### Specific Behavior
When dealing with many providers at once, various aspects of those providers need to be "combined" into one unified view.
For example, each internal provider has a "status", which should influence the Multi-Provider's overall "status".
The specific aspects that need to be addressed are described below.
#### Unique Names
In order to identify each provider uniquely, it must have a name associated with it.
The unique name will be used when reporting errors and results in order to indicate from which provider they came from.
Most providers have a `metadata.name` field which could be used, but this would be non-unique in the case where two instances of the same type of provider are used. As a result there would need to be a way to differentiate the two.
When instantiating the Multi-Provider, there will be an option for specifying a name to associate to each provider:
```typescript
const multiProvider = new MultiProvider([
{
provider: new ProviderA(),
name: "ProviderA"
},
{
provider: new ProviderB(),
name: "ProviderB"
}
])
await OpenFeature.setProviderAndWait(multiProvider)
```
Names for each provider are then determined like this:
1. name passed in to constructor if specified
2. `metadata.name` if it is unique among providers
3. `${metadata.name}_${index}` if name is not unique. Eg. the first instance of ProviderA provider might be named "providerA_1" and the second might be "providerA_2"
If multiple names are passed in the constructor which conflict, an error will be thrown.
#### Initialization
Initialization of each provider should be handled in parallel in the Multi-Provider's `initialize` method.
It should call `initialize` on each provider it is managing, and bubble up any error that is thrown by re-throwing to the client.
#### Status and Event Handling
The status of a provider is tracked in OpenFeature SDKs based on emitted events.
Provider states can be transitioned in the ways represented here:
[https://openfeature.dev/specification/sections/flag-evaluation#17-provider-lifecycle-management](https://openfeature.dev/specification/sections/flag-evaluation#17-provider-lifecycle-management)
The SDK client tracks statuses of a provider as follows:
- Initially the status is `NOT_READY`
- Initialize function is called (if exists) and result is awaited
- Successful initialize transitions state to `READY`, error result transitions to either `ERROR` or `FATAL`
- From this point onwards, status is only changed as a result of provider emitting events to indicate status-changing things have occurred.
- It can emit events like `FATAL`, `ERROR`, `STALE` and `READY` to transition to those states.
The only statuses which affect evaluation behavior at the SDK client level are `FATAL` and `NOT_READY`.
If a provider is in either of these states, evaluation will be "skipped" by the client and the default value will be returned.
Other statuses are currently "informational". Nevertheless, the Multi-Provider will represent an overall "status" based on the combined statuses of the providers.
##### Multi-Provider Status
The Multi-Provider mimics the event handling logic that tracks statuses in the SDK, and keeps track of the status of each provider it is managing.
The individual status-changing events from these providers will be "captured" in the Multi-Provider, and not re-emitted to the outer SDK UNLESS they cause the status of the Multi-Provider to change.
The status of the Multi-Provider will change when one of its providers changes to a status that is considered higher "precedence" than the current status.
The precedence order is defined as:
- FATAL
- NOT_READY
- ERROR
- STALE
- READY
For example, if all providers are currently in `READY` status, the Multi-Provider will be in `READY` status.
If one of the providers is `STALE`, the status of the Multi-Provider will be `STALE`.
If a different provider now becomes `ERROR`, the status will be `ERROR` even if the other provider is still in `STALE`.
When the Multi-Provider changes status, it does so by emitting the appropriate event to the SDK.
The "details" of that event will be **identical** to the details of the original event from one of the inner providers which triggered this state change.
There is another event called "configuration changed" which does not affect status.
This event should be re-emitted any time it occurs from any provider.
#### Evaluation Result
The evaluation result is based on the results from evaluating each provider.
There are multiple "strategies" configurable in the Multi-Provider to decide how to use the results.
#### Track Method Support
The Multi-Provider implements the `track` method from the OpenFeature specification, allowing tracking events to be sent across multiple underlying providers.
By default, tracking events are sent to all providers that are in `READY` status.
Providers in `NOT_READY` or `FATAL` states are automatically skipped for tracking operations.
Key features of tracking support include:
- **Error Resilience**: Individual provider tracking failures do not break the overall tracking flow - errors are logged but do not throw exceptions
- **Status Awareness**: Providers in `NOT_READY` or `FATAL` status will not recieve tracking events.
- **Strategy Integration**: Custom strategies can control which providers receive tracking calls using the `shouldTrackWithThisProvider` method
- **Graceful Degradation**: Providers that don't implement the `track` method are skipped
Example usage:
```typescript
const multiProvider = new MultiProvider([
{ provider: new ProviderA() },
{ provider: new ProviderB() }
])
await OpenFeature.setProviderAndWait(multiProvider)
const client = OpenFeature.getClient()
// Track events across all ready providers
client.track('purchase', { targetingKey: 'user123' }, { value: 99.99, currency: 'USD' })
```
For custom tracking behavior, strategies can implement the `shouldTrackWithThisProvider` method to selectively track with specific providers based on event name, context, or provider characteristics.
#### Interpreting Errors
Currently, providers have multiple ways of signalling evaluation errors to the SDK.
Particularly in the case of Javascript, a provider can return an evaluation result that contains an error code and message, but still has a "value" for the result. It can also throw an error.
Several providers currently use the former approach for indicating errors in operations, and use the `value` field of the result to return the default value from the provider itself.
For the purposes of aggregating providers, the Multi-Provider treats both thrown and returned errors as an "error" result. If the returned error result has a value, that value will be ignored by all strategies. Only "nominal" evaluation results will be considered by the evaluation.
### Strategies
The Multi-Provider supports multiple ways of deciding how to evaluate the set of providers it is managing, and how to deal with any errors that are thrown.
Strategies must be adaptable to the various requirements that might be faced in a multi-provider situation.
In some cases, the strategy may want to ignore errors from individual providers as long as one of them successfully responds.
In other cases, it may want to evaluate providers in order and skip the rest if a successful result is obtained.
In still other scenarios, it may be required to always call every provider and decide what to do with the set of results.
The strategy to use is passed in to the Multi-Provider constructor as follows:
```typescript
new MultiProvider(
[
{
provider: new ProviderA()
},
{
provider: new ProviderB()
}
],
new FirstMatchStrategy()
)
```
By default, the Multi-Provider uses the `FirstMatchStrategy`.
Here are some standard strategies that come with the Multi-Provider:
#### First Match Strategy
Return the first result returned by a provider.
Skip providers that indicate they had no value due to `FLAG_NOT_FOUND`.
In all other cases, use the value returned by the provider.
If any provider returns an error result other than `FLAG_NOT_FOUND`, the whole evaluation should error and "bubble up" the individual provider's error in the result.
As soon as a value is returned by a provider, the rest of the operation should short-circuit and not call the rest of the providers.
[See the refrence implementation](https://github.com/open-feature/js-sdk-contrib/blob/main/libs/providers/multi-provider/src/lib/strategies/FirstMatchStrategy.ts)
#### First Successful Strategy
Similar to "First Match", except that errors from evaluated providers do not halt execution.
Instead, it will return the first successful result from a provider. If no provider successfully responds, it will throw an error result.
[See the refrence implementation](https://github.com/open-feature/js-sdk-contrib/blob/main/libs/providers/multi-provider/src/lib/strategies/FirstSuccessfulStrategy.ts)
#### Comparison Strategy
Require that all providers agree on a value.
If every provider returns a non-error result, and the values do not agree, the Multi-Provider should return the result from a configurable "fallback" provider.
It will also call an optional "onMismatch" callback that can be used to monitor cases where mismatches of evaluation occurred.
Otherwise the value of the result will be the result of the first provider in precedence order.
[See the refrence implementation](https://github.com/open-feature/js-sdk-contrib/blob/main/libs/providers/multi-provider/src/lib/strategies/ComparisonStrategy.ts)
#### User Defined Custom Strategy
Rather than making assumptions about when to use a provider's result and when not to (which may not hold across all providers) there is also a way for the user to define their own strategy that determines whether or not to use a result or fall through to the next one.
A strategy can be implemented by implementing the `BaseEvaluationStrategy` class as follows:
```typescript
type StrategyEvaluationContext = {
flagKey: string;
flagType: FlagValueType;
};
type StrategyPerProviderContext = StrategyEvaluationContext & {
provider: Provider;
providerName: string;
providerStatus: ProviderStatus;
};
type ProviderResolutionResult<T extends FlagValue> = {
details: ResolutionDetails<T>;
thrownError?: unknown;
provider: Provider;
providerName: string;
}
type FinalResult = {
details?: ResolutionDetails<unknown>;
provider?: Provider;
providerName?: string;
errors?: {
providerName: string;
error: unknown;
}[];
};
abstract class BaseEvaluationStrategy {
runMode: 'parallel' | 'sequential'
abstract shouldEvaluateThisProvider(
strategyContext: StrategyPerProviderContext,
evalContext: EvaluationContext
): boolean;
abstract shouldEvaluateNextProvider<T extends FlagValue>(
strategyContext: StrategyPerProviderContext,
context: EvaluationContext,
result: ProviderResolutionResult<T>
): boolean;
abstract determineFinalResult<T extends FlagValue>(
strategyContext: StrategyEvaluationContext,
context: EvaluationContext,
resolutions: ProviderResolutionResult<T>[],
): FinalResult;
abstract shouldTrackWithThisProvider(
strategyContext: StrategyProviderContext,
context: EvaluationContext,
trackingEventName: string,
trackingEventDetails: TrackingEventDetails,
): boolean;
}
```
- **`runMode`**: property defines whether the providers will all be evaluated at once in `parallel`, or whether they will be evaluated `sequentially` with each result determining whether to evaluate the next one in order.
- **`shouldEvaluateThisProvider`**: function is called for each provider right before the Multi-Provider would evaluate it.
- If the function returns false, the provider will be skipped.
- This can be useful in cases where it's desired to skip a provider based on what flag key is being used, or based on some state from the provider itself that indicates it shouldn't be evaluated right now.
- **`shouldEvaluateNextProvider`**: function is called right after a provider is evaluated.
- It is called with the details of resolution or any error that was thrown (which will be caught).
- If the function returns true, the next provider will be called.
- Otherwise all remaining providers will be skipped and the results of the ones that have been evaluated so far will be passed to `determineFinalResult` .
- If this function throws an error, the Multi-Provider will throw an error and not evaluate further providers.
- This function is not called when `runMode` is `parallel`, since all providers will be executed (as long as they individually pass the `shouldEvaluateThisProvider` check)
- **`determineFinalResult`**: function is called after the resolution stage if no further providers will be called.
- This function can be used to decide from the set of resolutions which one should ultimately be used.
- The function must return a `FinalResult` object which contains the final `ResolutionDetails` and the provider that they correspond to, or an array of `errors` in the case of a non-successful result, with the provider that created each error.
- **`shouldTrackWithThisProvider`**: function is called when the `track()` method is called on the SDK.
- This function can be used to decide which providers should recieve `track()` events.
- Return `true` to send the tracking event to the provider, `false` to skip it.
To see [reference implementations](https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/multi-provider/src/lib/strategies) of the above-mentioned strategies.
### Hooks
Provider hooks are capable of modifying the context before an evaluation takes place.
This behavior must be preserved, but it's also necessary to prevent these hooks from interfering with the context being passed to other providers.
For this reason, the Multi-Provider manages calling the hooks of each provider itself, at the appropriate time.
It then uses the result of the before hooks for a given provider as the new evaluation context when evaluating **that provider**, without affecting the context used for other providers.
It then calls the after, error and finally hooks using the appropriate context as well.
Errors thrown from these hooks are be bubbled up to the client, depending on how the evaluation "strategy" defines what to do with errors.
### Shutdown
The shutdown method should ensure that `shutdown()` is called in all underlying providers, and bubble up any errors to the client
### Error Handling
In cases where all providers are being called (Evaluation etc.) there may be more than one error encountered from more than one provider.
The Multi-Provider will collect and throw all errors in an aggregated form as follows:
```javascript
error = {
message: 'some message',
code: SOME_ERROR,
// which provider caused the error
originalErrors: [
{
source: 'ProviderA',
error: {
message: 'something',
}
}
]
}
```
In the case where only one error is thrown by one provider, it will still throw in this form for consistency.
Other errors from the Multi-Provider itself will use standard error types.
### Metadata
Providers can contain metadata. The Multi-Provider will make that metadata available within its own metadata as follows:
```javascript
{
name: 'multiprovider',
originalMetadata: {
providerA: {...},
providerB: {...}
},
}
```
## Logging Hook
> OpenFeature SDK implementations **SHOULD** provide a `logging hook`.
The logging hook is a hook which logs messages during the flag evaluation life-cycle as described below:
| Stage | Logged data |
| ------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| before | `stage`, `domain`, `provider_name`, `flag_key`, `default_value` and `evaluation_context` (serialized, opt-in) |
| after | `stage`, `domain`, `provider_name`, `flag_key`, `default_value`, `evaluation_context` (serialized, opt-in), `reason`, `variant` and `value` |
| error | `stage`, `domain`, `provider_name`, `flag_key`, `default_value`, `evaluation_context` (serialized, opt-in), `error_code`, and `error_message` |
| finally | N/A |
> The evaluation context **SHOULD** only be logged if an associated option indicates so.
This can be a constructor option or similar, for example: `boolean printContext`.
> If logging the evaluation context is enabled, it **MUST** be printed in such a way that it's human readable.
Consider printing the evaluation context as a stringified JSON object, or using some other format that allows the nested properties to be easily read.
> If the logger abstraction in the SDK supports a log level concept, the appropriate log level **SHOULD** be used for each stage (before/after: debug/info, error: error).
Consider using `debug` or `info` levels for the `before` and `after` stages, and the `error` level for the `error` stage.

View File

@ -0,0 +1,16 @@
---
id: appendix-b
title: "Appendix B: Gherkin Suites"
description: A Set of End-to-End Tests for Validating OpenFeature Implementations
sidebar_position: 5
---
# Appendix B: Gherkin Suites
This section contains a set of language-agnostic end-to-end tests (defined in gherkin).
These tests can be used to validate the behavior of an OpenFeature implementation.
"Features" (test suites) can be used in conjunction with an [in-memory provider](./appendix-a-included-utilities.md#in-memory-provider) and a cucumber test-runner for the language in question.
## Evaluation Feature
The [evaluation feature](./assets/gherkin/evaluation.feature) contains tests for the basic functionality of the [Evaluation API](./sections/01-flag-evaluation.md).

View File

@ -0,0 +1,19 @@
---
id: appendix-c
title: "Appendix C: OpenFeature Remote Evaluation Protocol"
description: A unified protocol for feature flagging
sidebar_position: 5
---
# Appendix C: OpenFeature Remote Evaluation Protocol
OpenFeature Remote Evaluation Protocol (OFREP) is a unified feature flag evaluation protocol that adheres to the OpenFeature semantics.
You can find proposals, discussions and the progress at the dedicated [OpenFeature OFREP](https://github.com/open-feature/protocol) repository.
There is a dedicated [OpenFeature working group](https://github.com/open-feature/community/blob/main/config/open-feature/spec-evaluation/workgroup.yaml) for this initiative that focuses on:
- Feature flag evaluation protocol
- Telemetry enrichment of the evaluations
- Reference implementations of the protocol
If you are interested in this initiative, you can join the [`#openfeature-remote-evaluation-protocol`](https://cloud-native.slack.com/archives/C066A48LK35) Slack channel on the [CNCF Slack](https://communityinviter.com/apps/cloud-native/cncf) workspace.

View File

@ -0,0 +1,73 @@
---
id: appendix-d
title: "Appendix D: Observability"
description: Conventions for OpenFeature telemetry signals
sidebar_position: 5
---
# Appendix D: Observability
This document describes conventions for extracting data from the OpenFeature SDK for use in telemetry signals.
It primarily focuses on providing recommendations for mapping well-known fields in OpenFeature to [OpenTelemetry feature-flag log records][otel-ff-logs] and other semantic conventions.
## Evaluations
Flag evaluation telemetry comprises data resolved from the provider resolution (evaluation details and flag metadata) as well as metadata about the provider itself.
This is particularly relevant to telemetry-related [hooks](./sections/04-hooks.md).
### Evaluation Details
The following describes how fields on the [evaluation details](types.md#evaluation-details) are mapped to feature flag log records:
| Log Record Attribute | Source Field or Derived Value from Evaluation Details | Requirement level | Type | Notes |
| ----------------------------- | ----------------------------------------------------- | ----------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------- |
| `feature_flag.key` | `flag key` | `Required` | `string` | See: [flag key](./glossary.md#flag-key) |
| `feature_flag.result.variant` | `variant` | `Conditionally Required` [^1] | `string` | See: [variant](./glossary.md#variant) |
| `feature_flag.result.value` | `value` | `Conditionally Required` [^2] | `undefined` | See: [value](./glossary.md#values) |
| `feature_flag.result.reason` | `reason` | `Recommended` | `string` | See: [reason](./types.md#resolution-reason) |
| `error.type` | `error code` | `Conditionally Required` [^3] | `string` | See: [error code](./types.md#error-code), |
| `error.message` | `error message` | `Conditionally Required` [^3] | `string` | A human-readable error message associated with a failed evaluation. For programmatic purposes, refer to `error code`. |
> [!NOTE]
> The `error.type` and `feature_flag.result.reason` enumerations use a lowercase "snake_case" convention (see [OpenTelemetry feature-flag log records][otel-ff-logs]).
> OpenFeature [error codes](types.md#error-code) and [resolution reasons](./types.md#resolution-reason) should be transformed accordingly by integrations which include this data.
### Flag Metadata
The following describes how keys in [flag metadata](types.md#flag-metadata) are mapped to feature flag log records:
| Log Record Attribute | Flag Metadata Key | Requirement level | Type | Notes |
| ------------------------- | ----------------- | ----------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `feature_flag.context.id` | `contextId` | `Recommended` | `string` | The context identifier returned in the flag metadata uniquely identifies the subject of the flag evaluation. If not available, the [targeting key](./glossary.md#targeting-key) should be used. |
| `feature_flag.set.id` | `flagSetId` | `Recommended` | `string` | A logical identifier for the [flag set](./glossary.md#flag-set). |
| `feature_flag.version` | `version` | `Recommended` | `string` | A version string (format unspecified) for the flag or [flag set](./glossary.md#flag-set). |
> [!NOTE]
> Keys in flag metadata use the "camelCase" casing convention, while the OpenTelemetry standard uses a namespaced "snake_case" convention.
### Provider Metadata
| Log Record Attribute | Provider Metadata Field | Requirement level | Type | Notes |
| ---------------------------- | ----------------------- | ----------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `feature_flag.provider.name` | `name` | `Recommended` | `string` | The name of the provider as defined in the `provider metadata`, available in the `hook context`. |
## History
Feature flags in the OpenTelemetry semantic conventions are currently in development and are marked as experimental.
The following table describes the history of changes to the OpenTelemetry feature flag log records as it progresses towards a stable release.
| Original Field Name | New Field Name | Semantic Convention Release |
| --------------------------------------- | ----------------------------- | -------------------------------------------------------------------------------------- |
| `feature_flag.variant` | `feature_flag.result.variant` | [v1.32.0](https://github.com/open-telemetry/semantic-conventions/releases/tag/v1.32.0) |
| `feature_flag.evaluation.reason` | `feature_flag.result.reason` | [v1.32.0](https://github.com/open-telemetry/semantic-conventions/releases/tag/v1.32.0) |
| `feature_flag.evaluation.error.message` | `error.message` | [v1.33.0](https://github.com/open-telemetry/semantic-conventions/releases/tag/v1.33.0) |
| `feature_flag.provider_name` | `feature_flag.provider.name` | [v1.33.0](https://github.com/open-telemetry/semantic-conventions/releases/tag/v1.33.0) |
| `value` | `feature_flag.result.value` | [v1.34.0](https://github.com/open-telemetry/semantic-conventions/releases/tag/v1.34.0) |
## Footnotes
[^1]: The `variant` field should be included whenever possible as it represents the symbolic name of the flag's returned value (e.g., "on"/"off", "control"/"treatment"). Only omit if the provider doesn't supply this information.
[^2]: The `value` field should be included whenever a `variant` is unavailable. Large or sensitive values should be redacted or omitted prior to being captured in telemetry signals.
[^3]: Include `error.type` and `error.message`, if and only if an error occurred during a flag evaluation.
[otel-ff-logs]: https://opentelemetry.io/docs/specs/semconv/feature-flags/feature-flags-logs/

View File

@ -0,0 +1,128 @@
Feature: Context merging precedence
Background:
Given a stable provider with retrievable context is registered
Scenario Outline: A context entry is added to a single level
Given A context entry with key "key" and value "value" is added to the "<level>" level
When Some flag was evaluated
Then The merged context contains an entry with key "key" and value "value"
@transaction
Examples:
| level |
| API |
| Transaction |
| Client |
| Invocation |
@hooks
Examples:
| level |
| API |
| Client |
| Invocation |
| Before Hooks |
@hooks @transaction
Examples:
| level |
| API |
| Transaction |
| Client |
| Invocation |
| Before Hooks |
@transaction
Scenario: For a transaction, a context entry is added to each level with different keys
Given A context entry with key "API" and value "API value" is added to the "API" level
And A context entry with key "Transaction" and value "Transaction value" is added to the "Transaction" level
And A context entry with key "Client" and value "Client value" is added to the "Client" level
And A context entry with key "Invocation" and value "Invocation value" is added to the "Invocation" level
When Some flag was evaluated
Then The merged context contains an entry with key "API" and value "API value"
And The merged context contains an entry with key "Transaction" and value "Transaction value"
And The merged context contains an entry with key "Client" and value "Client value"
And The merged context contains an entry with key "Invocation" and value "Invocation value"
@hooks
Scenario: For a hook, a context entry is added to each level with different keys
Given A context entry with key "API" and value "API value" is added to the "API" level
And A context entry with key "Client" and value "Client value" is added to the "Client" level
And A context entry with key "Invocation" and value "Invocation value" is added to the "Invocation" level
And A context entry with key "Before Hooks" and value "Before Hooks value" is added to the "Before Hooks" level
When Some flag was evaluated
Then The merged context contains an entry with key "API" and value "API value"
And The merged context contains an entry with key "Client" and value "Client value"
And The merged context contains an entry with key "Invocation" and value "Invocation value"
And The merged context contains an entry with key "Before Hooks" and value "Before Hooks value"
@hooks @transaction
Scenario: For a transaction and a hook, a context entry is added to each level with different keys
Given A context entry with key "API" and value "API value" is added to the "API" level
And A context entry with key "Transaction" and value "Transaction value" is added to the "Transaction" level
And A context entry with key "Client" and value "Client value" is added to the "Client" level
And A context entry with key "Invocation" and value "Invocation value" is added to the "Invocation" level
And A context entry with key "Before Hooks" and value "Before Hooks value" is added to the "Before Hooks" level
When Some flag was evaluated
Then The merged context contains an entry with key "API" and value "API value"
And The merged context contains an entry with key "Transaction" and value "Transaction value"
And The merged context contains an entry with key "Client" and value "Client value"
And The merged context contains an entry with key "Invocation" and value "Invocation value"
And The merged context contains an entry with key "Before Hooks" and value "Before Hooks value"
@transaction
Scenario Outline: For a transaction, a context entry in one level overwrites values with the same key from preceding levels
Given A table with levels of increasing precedence
| API |
| Transaction |
| Client |
| Invocation |
And Context entries for each level from API level down to the "<level>" level, with key "key" and value "<level>"
When Some flag was evaluated
Then The merged context contains an entry with key "key" and value "<level>"
Examples:
| level |
| API |
| Transaction |
| Client |
| Invocation |
@hooks
Scenario Outline: For a hook, a context entry in one level overwrites values with the same key from preceding levels
Given A table with levels of increasing precedence
| API |
| Client |
| Invocation |
| Before Hooks |
And Context entries for each level from API level down to the "<level>" level, with key "key" and value "<level>"
When Some flag was evaluated
Then The merged context contains an entry with key "key" and value "<level>"
Examples:
| level |
| API |
| Client |
| Invocation |
| Before Hooks |
@hooks @transaction
Scenario Outline: For a transaction and a hook, context entry in one level overwrites values with the same key from preceding levels
Given A table with levels of increasing precedence
| API |
| Transaction |
| Client |
| Invocation |
| Before Hooks |
And Context entries for each level from API level down to the "<level>" level, with key "key" and value "<level>"
When Some flag was evaluated
Then The merged context contains an entry with key "key" and value "<level>"
Examples:
| level |
| API |
| Transaction |
| Client |
| Invocation |
| Before Hooks |

View File

@ -0,0 +1,67 @@
Feature: Flag evaluation
# This test suite contains scenarios to test the flag evaluation API.
Background:
Given a stable provider
# basic evaluation
Scenario: Resolves boolean value
When a boolean flag with key "boolean-flag" is evaluated with default value "false"
Then the resolved boolean value should be "true"
Scenario: Resolves string value
When a string flag with key "string-flag" is evaluated with default value "bye"
Then the resolved string value should be "hi"
Scenario: Resolves integer value
When an integer flag with key "integer-flag" is evaluated with default value 1
Then the resolved integer value should be 10
Scenario: Resolves float value
When a float flag with key "float-flag" is evaluated with default value 0.1
Then the resolved float value should be 0.5
Scenario: Resolves object value
When an object flag with key "object-flag" is evaluated with a null default value
Then the resolved object value should be contain fields "showImages", "title", and "imagesPerPage", with values "true", "Check out these pics!" and 100, respectively
# detailed evaluation
Scenario: Resolves boolean details
When a boolean flag with key "boolean-flag" is evaluated with details and default value "false"
Then the resolved boolean details value should be "true", the variant should be "on", and the reason should be "STATIC"
Scenario: Resolves string details
When a string flag with key "string-flag" is evaluated with details and default value "bye"
Then the resolved string details value should be "hi", the variant should be "greeting", and the reason should be "STATIC"
Scenario: Resolves integer details
When an integer flag with key "integer-flag" is evaluated with details and default value 1
Then the resolved integer details value should be 10, the variant should be "ten", and the reason should be "STATIC"
Scenario: Resolves float details
When a float flag with key "float-flag" is evaluated with details and default value 0.1
Then the resolved float details value should be 0.5, the variant should be "half", and the reason should be "STATIC"
Scenario: Resolves object details
When an object flag with key "object-flag" is evaluated with details and a null default value
Then the resolved object details value should be contain fields "showImages", "title", and "imagesPerPage", with values "true", "Check out these pics!" and 100, respectively
And the variant should be "template", and the reason should be "STATIC"
# context-aware evaluation
Scenario: Resolves based on context
When context contains keys "fn", "ln", "age", "customer" with values "Sulisław", "Świętopełk", 29, "false"
And a flag with key "context-aware" is evaluated with default value "EXTERNAL"
Then the resolved string response should be "INTERNAL"
And the resolved flag value is "EXTERNAL" when the context is empty
# errors
Scenario: Flag not found
When a non-existent string flag with key "missing-flag" is evaluated with details and a default value "uh-oh"
Then the default string value should be returned
And the reason should indicate an error and the error code should indicate a missing flag with "FLAG_NOT_FOUND"
Scenario: Type error
When a string flag with key "wrong-flag" is evaluated as an integer, with details and a default value 13
Then the default integer value should be returned
And the reason should indicate an error and the error code should indicate a type mismatch with "TYPE_MISMATCH"

View File

@ -0,0 +1,49 @@
@hooks
Feature: Evaluation details through hooks
# This test suite contains scenarios to test the functionality of hooks.
Background:
Given a stable provider
Scenario: Passes evaluation details to after and finally hooks
Given a client with added hook
And a boolean-flag with key "boolean-flag" and a default value "false"
When the flag was evaluated with details
Then the "before" hook should have been executed
And the "after, finally" hooks should be called with evaluation details
| data_type | key | value |
| string | flag_key | boolean-flag |
| boolean | value | true |
| string | variant | on |
| string | reason | STATIC |
| string | error_code | null |
# errors
Scenario: Flag not found
Given a client with added hook
And a string-flag with key "missing-flag" and a default value "uh-oh"
When the flag was evaluated with details
Then the "before" hook should have been executed
And the "error" hook should have been executed
And the "finally" hooks should be called with evaluation details
| data_type | key | value |
| string | flag_key | missing-flag |
| string | value | uh-oh |
| string | variant | null |
| string | reason | ERROR |
| string | error_code | FLAG_NOT_FOUND |
Scenario: Type error
Given a client with added hook
And a boolean-flag with key "wrong-flag" and a default value "false"
When the flag was evaluated with details
Then the "before" hook should have been executed
And the "error" hook should have been executed
And the "finally" hooks should be called with evaluation details
| data_type | key | value |
| string | flag_key | wrong-flag |
| boolean | value | false |
| string | variant | null |
| string | reason | ERROR |
| string | error_code | TYPE_MISMATCH |

View File

@ -0,0 +1,27 @@
@Metadata
Feature: Metadata
Background:
Given a stable provider
Scenario: Returns metadata
Given a Boolean-flag with key "metadata-flag" and a default value "true"
When the flag was evaluated with details
Then the resolved metadata should contain
| key | metadata_type | value |
| string | String | 1.0.2 |
| integer | Integer | 2 |
| float | Float | 0.1 |
| boolean | Boolean | true |
Scenario Outline: Returns no metadata
Given a <flag_type>-flag with key "<key>" and a default value "<default_value>"
When the flag was evaluated with details
Then the resolved metadata is empty
Examples: Flags
| key | flag_type | default_value |
| boolean-flag | Boolean | true |
| integer-flag | Integer | 23 |
| float-flag | Float | 2.3 |
| string-flag | String | value |

View File

@ -1,613 +0,0 @@
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"type": "text",
"version": 441,
"versionNonce": 630753906,
"isDeleted": false,
"id": "fWR53sO5RGQE_NukjZPMN",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dashed",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 890,
"y": 1000,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 260,
"height": 25,
"seed": 1929169548,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1657721892932,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "Flag Evaluation Life Cycle",
"baseline": 18,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "Flag Evaluation Life Cycle"
},
{
"type": "arrow",
"version": 209,
"versionNonce": 98635182,
"isDeleted": false,
"id": "comxMv7YbRFpt1dbYHSw9",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"angle": 0,
"x": 756.7414572451423,
"y": 849,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 0.7414572451423282,
"height": 132,
"seed": 2005717004,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1657721892932,
"link": null,
"locked": false,
"startBinding": {
"elementId": "0SyqzRLd6i14ANI4cp-V3",
"focus": 0.010928467393289114,
"gap": 3.5
},
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
-0.7414572451423282,
-132
]
]
},
{
"type": "arrow",
"version": 285,
"versionNonce": 247622706,
"isDeleted": false,
"id": "f6SHZ953dRbLcslg-oHpV",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dashed",
"roughness": 0,
"opacity": 100,
"angle": 0,
"x": 428,
"y": 700,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 1188,
"height": 136,
"seed": 482789300,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1657721892932,
"link": null,
"locked": false,
"startBinding": null,
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
328,
0
],
[
592,
-126
],
[
840,
4
],
[
1188,
10
]
]
},
{
"type": "arrow",
"version": 170,
"versionNonce": 101656558,
"isDeleted": false,
"id": "hOQcNNx7KUdARzsDabH3G",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dotted",
"roughness": 0,
"opacity": 100,
"angle": 0,
"x": 764,
"y": 709,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 488,
"height": 122,
"seed": 1987572492,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1657721892932,
"link": null,
"locked": false,
"startBinding": null,
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
236,
120
],
[
488,
-2
]
]
},
{
"type": "text",
"version": 480,
"versionNonce": 63575538,
"isDeleted": false,
"id": "0SyqzRLd6i14ANI4cp-V3",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dashed",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 680,
"y": 852.5,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 152,
"height": 25,
"seed": 1635894540,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [
{
"id": "comxMv7YbRFpt1dbYHSw9",
"type": "arrow"
}
],
"updated": 1657721892932,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "Flag evaluation",
"baseline": 18,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "Flag evaluation"
},
{
"type": "text",
"version": 659,
"versionNonce": 100661806,
"isDeleted": false,
"id": "DeLSf4xf9PqFaF3jbukNq",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dashed",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 510,
"y": 582.25,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 146,
"height": 25,
"seed": 1876044300,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [
{
"id": "F3WMEgn3efPIVfvbnc7Ip",
"type": "arrow"
}
],
"updated": 1657721892932,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "\"Before\" stage",
"baseline": 18,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "\"Before\" stage"
},
{
"type": "text",
"version": 60,
"versionNonce": 1438025650,
"isDeleted": false,
"id": "yEi8T8PClIvN-AkBfLMYy",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dashed",
"roughness": 0,
"opacity": 100,
"angle": 0,
"x": 940,
"y": 926,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 131,
"height": 25,
"seed": 397093556,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [
{
"id": "rldjo9Bs_0OE8EKg7WDAB",
"type": "arrow"
}
],
"updated": 1657721892932,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "\"Error\" stage",
"baseline": 18,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "\"Error\" stage"
},
{
"type": "text",
"version": 91,
"versionNonce": 1822196846,
"isDeleted": false,
"id": "5wDUlPd8B_jixs5NBT90L",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dashed",
"roughness": 0,
"opacity": 100,
"angle": 0,
"x": 942,
"y": 450,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 134,
"height": 25,
"seed": 1807170996,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [
{
"id": "mApSEU77V3z0-ar-pavXA",
"type": "arrow"
}
],
"updated": 1657721892932,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "\"After\" stage",
"baseline": 18,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "\"After\" stage"
},
{
"type": "text",
"version": 64,
"versionNonce": 481214834,
"isDeleted": false,
"id": "ODtqRP1bT-9qVF3QdR4Zz",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dashed",
"roughness": 0,
"opacity": 100,
"angle": 0,
"x": 1374,
"y": 590,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 139,
"height": 25,
"seed": 1331492620,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [
{
"id": "JLdSBG-06gcICnQFbizDD",
"type": "arrow"
}
],
"updated": 1657721892932,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "\"Finally\" stage",
"baseline": 18,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "\"Finally\" stage"
},
{
"type": "arrow",
"version": 58,
"versionNonce": 1538115246,
"isDeleted": false,
"id": "JLdSBG-06gcICnQFbizDD",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"angle": 0,
"x": 1444,
"y": 627,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 0,
"height": 82,
"seed": 1045076748,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1657721892932,
"link": null,
"locked": false,
"startBinding": {
"elementId": "ODtqRP1bT-9qVF3QdR4Zz",
"focus": -0.007194244604316547,
"gap": 12
},
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
0,
82
]
]
},
{
"type": "arrow",
"version": 54,
"versionNonce": 1408877362,
"isDeleted": false,
"id": "mApSEU77V3z0-ar-pavXA",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"angle": 0,
"x": 1016,
"y": 483,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 2,
"height": 80,
"seed": 1589915700,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1657721892932,
"link": null,
"locked": false,
"startBinding": {
"elementId": "5wDUlPd8B_jixs5NBT90L",
"focus": -0.09637883008356547,
"gap": 8
},
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
2,
80
]
]
},
{
"type": "arrow",
"version": 51,
"versionNonce": 1406976238,
"isDeleted": false,
"id": "rldjo9Bs_0OE8EKg7WDAB",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"angle": 0,
"x": 1008,
"y": 915,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 2,
"height": 78,
"seed": 1974872460,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1657721892932,
"link": null,
"locked": false,
"startBinding": {
"elementId": "yEi8T8PClIvN-AkBfLMYy",
"focus": 0.047136735488897546,
"gap": 11
},
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
-2,
-78
]
]
},
{
"type": "arrow",
"version": 61,
"versionNonce": 1979616498,
"isDeleted": false,
"id": "F3WMEgn3efPIVfvbnc7Ip",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"angle": 0,
"x": 584,
"y": 623,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 0,
"height": 78,
"seed": 939203124,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1657721892932,
"link": null,
"locked": false,
"startBinding": {
"elementId": "DeLSf4xf9PqFaF3jbukNq",
"focus": -0.0136986301369863,
"gap": 15.75
},
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
0,
78
]
]
},
{
"id": "NkDXWchoeeUIU--BNVMhT",
"type": "line",
"x": 581.5249175743113,
"y": 716.6987736687283,
"width": 410,
"height": 114,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dotted",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "round",
"seed": 407117490,
"version": 212,
"versionNonce": 140528046,
"isDeleted": false,
"boundElements": null,
"updated": 1657721903964,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
106,
60
],
[
276,
112
],
[
410,
114
]
],
"lastCommittedPoint": null,
"startBinding": null,
"endBinding": null,
"startArrowhead": null,
"endArrowhead": null
}
],
"appState": {
"gridSize": null,
"viewBackgroundColor": "#ffffff"
},
"files": {}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

View File

@ -1,431 +0,0 @@
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"id": "sFzbmoq8x0HyykU9HhKbf",
"type": "rectangle",
"x": 946,
"y": 235,
"width": 40,
"height": 360,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "sharp",
"seed": 97747724,
"version": 143,
"versionNonce": 1741772596,
"isDeleted": false,
"boundElements": [
{
"id": "GjpO8NH8C2w3d6tgZEw0a",
"type": "arrow"
},
{
"id": "YNZgdJRPYhxEAG9UTiiaX",
"type": "arrow"
}
],
"updated": 1656091357618,
"link": null,
"locked": false
},
{
"id": "AyfB6rhs9QP8dDml-jTtM",
"type": "diamond",
"x": 1104,
"y": 301,
"width": 230,
"height": 218,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "sharp",
"seed": 1169897996,
"version": 109,
"versionNonce": 1736986804,
"isDeleted": false,
"boundElements": [
{
"id": "2X5Ue2aHSLNyoaJiVed-6",
"type": "arrow"
},
{
"id": "GjpO8NH8C2w3d6tgZEw0a",
"type": "arrow"
}
],
"updated": 1656091357618,
"link": null,
"locked": false
},
{
"id": "ACCE9Pv-d-jAR2g3-eEcn",
"type": "text",
"x": 890,
"y": 400,
"width": 152,
"height": 25,
"angle": 1.55517259817442,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "sharp",
"seed": 398017588,
"version": 157,
"versionNonce": 1160434484,
"isDeleted": false,
"boundElements": null,
"updated": 1656092204660,
"link": null,
"locked": false,
"text": "Evaluation API",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"baseline": 18,
"containerId": null,
"originalText": "Evaluation API"
},
{
"id": "XTsFx3AIH7HTI_Sd1EtQQ",
"type": "text",
"x": 1180,
"y": 398,
"width": 80,
"height": 25,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "sharp",
"seed": 468363700,
"version": 111,
"versionNonce": 72997556,
"isDeleted": false,
"boundElements": null,
"updated": 1656092173540,
"link": null,
"locked": false,
"text": "Provider",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"baseline": 18,
"containerId": null,
"originalText": "Provider"
},
{
"id": "hk-L4sS1BEfbVBOgFS7g1",
"type": "text",
"x": 1478,
"y": 390,
"width": 164,
"height": 50,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "sharp",
"seed": 735367860,
"version": 227,
"versionNonce": 523408436,
"isDeleted": false,
"boundElements": null,
"updated": 1656092191304,
"link": null,
"locked": false,
"text": "Flag management\n system",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "top",
"baseline": 43,
"containerId": null,
"originalText": "Flag management\n system"
},
{
"id": "GjpO8NH8C2w3d6tgZEw0a",
"type": "arrow",
"x": 994,
"y": 413,
"width": 100,
"height": 2,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "round",
"seed": 2119779508,
"version": 144,
"versionNonce": 1893866804,
"isDeleted": false,
"boundElements": null,
"updated": 1656091357618,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
100,
-2
]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "sFzbmoq8x0HyykU9HhKbf",
"focus": -0.007982261640798228,
"gap": 8
},
"endBinding": {
"elementId": "AyfB6rhs9QP8dDml-jTtM",
"focus": 0.013761467889908258,
"gap": 7.6049836494533025
},
"startArrowhead": null,
"endArrowhead": "arrow"
},
{
"type": "arrow",
"version": 247,
"versionNonce": 1119338804,
"isDeleted": false,
"id": "2X5Ue2aHSLNyoaJiVed-6",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1342,
"y": 412,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 100,
"height": 2,
"seed": 1010136244,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1656091362726,
"link": null,
"locked": false,
"startBinding": {
"elementId": "AyfB6rhs9QP8dDml-jTtM",
"focus": 0.04091743119266055,
"gap": 6.954931105143189
},
"endBinding": {
"elementId": "OVJNNaVaqA4rfwXZ4m26c",
"focus": 0.043664558253417675,
"gap": 13
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
100,
-2
]
]
},
{
"type": "text",
"version": 315,
"versionNonce": 165520820,
"isDeleted": false,
"id": "j3MxwuU4vlV0o75SwNm6-",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 681.5,
"y": 400.5,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 103,
"height": 25,
"seed": 1401550644,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1656092169786,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "Application",
"baseline": 18,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "Application"
},
{
"type": "arrow",
"version": 202,
"versionNonce": 1322315020,
"isDeleted": false,
"id": "YNZgdJRPYhxEAG9UTiiaX",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 838,
"y": 413,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 100,
"height": 2,
"seed": 873715764,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1656091357618,
"link": null,
"locked": false,
"startBinding": {
"elementId": "FkWpwmnzGSQRmo6WPzc-b",
"focus": 0.01163027953478882,
"gap": 6
},
"endBinding": {
"elementId": "sFzbmoq8x0HyykU9HhKbf",
"focus": 0.025277161862527722,
"gap": 8
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
100,
-2
]
]
},
{
"id": "FkWpwmnzGSQRmo6WPzc-b",
"type": "rectangle",
"x": 630,
"y": 318,
"width": 202,
"height": 192,
"angle": 0,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"strokeSharpness": "sharp",
"seed": 694797236,
"version": 157,
"versionNonce": 46623628,
"isDeleted": false,
"boundElements": [
{
"id": "YNZgdJRPYhxEAG9UTiiaX",
"type": "arrow"
}
],
"updated": 1656091357619,
"link": null,
"locked": false
},
{
"type": "rectangle",
"version": 234,
"versionNonce": 914824204,
"isDeleted": false,
"id": "OVJNNaVaqA4rfwXZ4m26c",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1455,
"y": 316,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 202,
"height": 192,
"seed": 1478245516,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [
{
"id": "YNZgdJRPYhxEAG9UTiiaX",
"type": "arrow"
},
{
"id": "2X5Ue2aHSLNyoaJiVed-6",
"type": "arrow"
}
],
"updated": 1656091362726,
"link": null,
"locked": false
}
],
"appState": {
"gridSize": null,
"viewBackgroundColor": "#ffffff"
},
"files": {}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

View File

@ -1,27 +0,0 @@
# Evaluation Context
**Status**: [Experimental](./README.md#document-statuses)
## Overview
The `evaluation context` provides ambient information for the purposes of flag evaluation. Contextual data may be used as the basis for targeting, including rule-based evaluation, overrides for specific subjects, or fractional flag evaluation.
The context might contain information about the end-user, the application, the host, or any other ambient data that might be useful in flag evaluation. For example, a flag system might define rules that return a specific value based on the user's email address, locale, or the time of day. The context provides this information. The context can be optionally provided at evaluation, and mutated in [before hooks](./hooks.md).
### Fields
NOTE: Field casing is not specified, and should be chosen in accordance with language idioms.
see: [types](./types.md)
#### Requirement 3.1
> The `evaluation context` structure **MUST** define an optional `targeting key` field of type string, identifying the subject of the flag evaluation.
The targeting key uniquely identifies the subject (end-user, or client service) of a flag evaluation. Providers may require this field for fractional flag evaluation, rules, or overrides targeting specific users. Such providers may behave unpredictably if a targeting key is not specified at flag resolution.
#### Requirement 3.2
> The evaluation context **MUST** support the inclusion of custom fields, having keys of type `string`, and values of type `boolean | string | number | datetime | structure`.
see: [structure](./types.md#structure), [datetime](./types.md#datetime)

View File

@ -1,218 +0,0 @@
# Flag Evaluation API
**Status**: [Experimental](./README.md#document-statuses)
## Overview
The `evaluation API` allows for the evaluation of feature flag values, independent of any flag control plane or vendor. In the absence of a [provider](./providers.md) the `evaluation API` uses the "No-op provider", which simply returns the supplied default flag value.
### API Initialization and Configuration
#### Requirement 1.1.1
> The `API`, and any state it maintains **SHOULD** exist as a global singleton, even in cases wherein multiple versions of the `API` are present at runtime.
It's important that multiple instances of the `API` not be active, so that state stored therein, such as the registered `provider`, static global `evaluation context`, and globally configured `hooks` allow the `API` to behave predictably. This can be difficult in some runtimes or languages, but implementors should make their best effort to ensure that only a single instance of the `API` is used.
#### Requirement 1.1.2
> The `API` **MUST** provide a function to set the global `provider` singleton, which accepts an API-conformant `provider` implementation.
```typescript
// example provider mutator
OpenFeature.setProvider(new MyProvider());
```
See [provider](./providers.md) for details.
#### Requirement 1.1.3
> The `API` **MUST** provide a function to add `hooks` which accepts one or more API-conformant `hooks`, and appends them to the collection of any previously added hooks. When new hooks are added, previously added hooks are not removed.
```typescript
// example hook attachment
OpenFeature.addHooks([new MyHook()]);
```
See [hooks](./hooks.md) for details.
#### Requirement 1.1.4
> The API **MUST** provide a function for retrieving the metadata field of the configured `provider`.
```typescript
// example provider accessor
OpenFeature.getProviderMetadata();
```
See [provider](./providers.md) for details.
#### Requirement 1.1.5
> The `API` **MUST** provide a function for creating a `client` which accepts the following options:
>
> - name (optional): A logical string identifier for the client.
```typescript
// example client creation and retrieval
OpenFeature.getClient({
name: "my-openfeature-client",
});
```
The name is a logical identifier for the client.
#### Requirement 1.1.6
> The client creation function **MUST NOT** throw, or otherwise abnormally terminate.
Clients may be created in critical code paths, and even per-request in server-side HTTP contexts. Therefore, in keeping with the principle that OpenFeature should never cause abnormal execution of the first party application, this function should never throw. Abnormal execution in initialization should instead occur during provider registration.
### Client Usage
#### Requirement 1.2.1
> The client **MUST** provide a method to add `hooks` which accepts one or more API-conformant `hooks`, and appends them to the collection of any previously added hooks. When new hooks are added, previously added hooks are not removed.
```typescript
// example hook attachment
client.addHooks([new MyHook()]);
```
See [hooks](./hooks.md) for details.
#### Requirement 1.2.2
> The client interface **MUST** define a `metadata` member or accessor, containing an immutable `name` field or accessor of type string, which corresponds to the `name` value supplied during client creation.
```typescript
client.getMetadata().getName(); // "my-client"
```
#### Flag Evaluation
##### Requirement 1.3.1
> The `client` **MUST** provide methods for flag evaluation, with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), `evaluation context` (optional), and `evaluation options` (optional), which returns the flag value.
```typescript
// example flag evaluation
var myValue = client.getValue("my-flag", false);
```
##### Condition 1.3.2
> The language type system differentiates between strings, numbers, booleans and structures.
###### Conditional Requirement 1.3.2.1
> The `client` **MUST** provide methods for typed flag evaluation, including boolean, numeric, string, and structure.
```typescript
// example boolean flag evaluation
boolean myBool = client.getBooleanValue('bool-flag', false);
// example overloaded string flag evaluation with optional params
string myString = client.getStringValue('string-flag', 'N/A', evaluationContext, options);
// example number flag evaluation
number myNumber = client.getNumberValue('number-flag', 75);
// example overloaded structure flag evaluation with optional params
MyStruct myStruct = client.getObjectValue<MyStruct>('structured-flag', { text: 'N/A', percentage: 75 }, evaluationContext, options);
```
See [evaluation context](./evaluation-context.md) for details.
###### Conditional Requirement 1.3.3
> The `client` **SHOULD** guarantee the returned value of any typed flag evaluation method is of the expected type. If the value returned by the underlying provider implementation does not match the expected type, it's to be considered abnormal execution, and the supplied `default value` should be returned.
#### Detailed Flag Evaluation
##### Requirement 1.4.1
> The `client` **MUST** provide methods for detailed flag value evaluation with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), `evaluation context` (optional), and `evaluation options` (optional), which returns an `evaluation details` structure.
```typescript
// example detailed boolean flag evaluation
FlagEvaluationDetails<boolean> myBoolDetails = client.getBooleanDetails('bool-flag', false);
// example detailed string flag evaluation
FlagEvaluationDetails<string> myStringDetails = client.getStringDetails('string-flag', 'N/A', evaluationContext, options);
// example detailed number flag evaluation
FlagEvaluationDetails<number> myNumberDetails = client.getNumberDetails('number-flag', 75);
// example detailed structure flag evaluation
FlagEvaluationDetails<MyStruct> myStructDetails = client.getObjectDetails<MyStruct>('structured-flag', { text: 'N/A', percentage: 75 }, evaluationContext, options);
```
##### Requirement 1.4.2
> The `evaluation details` structure's `value` field **MUST** contain the evaluated flag value.
##### Condition 1.4.3
> The language supports generics (or an equivalent feature).
###### Conditional Requirement 1.4.3.1
> The `evaluation details` structure **SHOULD** accept a generic argument (or use an equivalent language feature) which indicates the type of the wrapped `value` field.
##### Requirement 1.4.4
> The `evaluation details` structure's `flag key` field **MUST** contain the `flag key` argument passed to the detailed flag evaluation method.
##### Requirement 1.4.5
> In cases of normal execution, the `evaluation details` structure's `variant` field **MUST** contain the value of the `variant` field in the `flag resolution` structure returned by the configured `provider`, if the field is set.
##### Requirement 1.4.6
> In cases of normal execution, the `evaluation details` structure's `reason` field **MUST** contain the value of the `reason` field in the `flag resolution` structure returned by the configured `provider`, if the field is set.
##### Requirement 1.4.7
> In cases of abnormal execution, the `evaluation details` structure's `error code` field **MUST** contain a string identifying an error occurred during flag evaluation and the nature of the error.
Some example error codes include: `"TARGETING_KEY_MISSING"`, `"PROVIDER_NOT_READY"`, `"FLAG_NOT_FOUND"`, `"PARSE_ERROR"`, `"TYPE_MISMATCH"`, or `"GENERAL"`.
##### Requirement 1.4.8
> In cases of abnormal execution (network failure, unhandled error, etc) the `reason` field in the `evaluation details` **SHOULD** indicate an error.
##### Requirement 1.4.9
> Methods, functions, or operations on the client **MUST NOT** throw exceptions, or otherwise abnormally terminate. Flag evaluation calls must always return the `default value` in the event of abnormal execution. Exceptions include functions or methods for the purposes for configuration or setup.
Configuration code includes code to set the provider, instantiate providers, and configure the global API object.
##### Requirement 1.4.10
> In the case of abnormal execution, the client **SHOULD** log an informative error message.
Implementations may define a standard logging interface that can be supplied as an optional argument to the client creation function, which may wrap standard logging functionality of the implementation language.
##### Requirement 1.4.11
> The `client` **SHOULD** provide asynchronous or non-blocking mechanisms for flag evaluation.
It's recommended to provide non-blocking mechanisms for flag evaluation, particularly in languages or environments wherein there's a single thread of execution.
#### Evaluation Options
##### Requirement 1.5.1
> The `evaluation options` structure's `hooks` field denotes an ordered collection of hooks that the client **MUST** execute for the respective flag evaluation, in addition to those already configured.
See [hooks](./hooks.md) for details.
#### Context Transformation
##### Requirement 1.6.1
> The `client` **SHOULD** transform the `evaluation context` using the `provider's` `context transformer` function if one is defined, before passing the result of the transformation to the provider's flag resolution functions.
See [context transformation](./providers.md#context-transformation) for details.

View File

@ -1,4 +1,10 @@
# Glossary
---
title: Glossary
description: A list of terms used within the OpenFeature specification.
sidebar_position: 1
---
# Glossary <!-- omit from toc -->
This document defines some terms that are used across this specification.
@ -16,16 +22,24 @@ This document defines some terms that are used across this specification.
- [Library Author](#library-author)
- [Common](#common)
- [Feature Flag SDK](#feature-flag-sdk)
- [Client-Side SDK](#client-side-sdk)
- [Server-Side SDK](#server-side-sdk)
- [Feature Flag API](#feature-flag-api)
- [Evaluation API](#evaluation-api)
- [Flag Management System](#flag-management-system)
- [Client](#client)
- [Provider](#provider)
- [Provider Lifecycle](#provider-lifecycle)
- [Domain](#domain)
- [Integration](#integration)
- [Evaluation Context](#evaluation-context)
- [Transaction Context Propagator](#transaction-context-propagator)
- [Evaluating Flag Values](#evaluating-flag-values)
- [Resolving Flag Values](#resolving-flag-values)
- [Tracking Event](#tracking-event)
- [Flagging specifics](#flagging-specifics)
- [Flag](#flag)
- [Flag Set](#flag-set)
- [Flag Key](#flag-key)
- [Variant](#variant)
- [Values](#values)
@ -33,6 +47,9 @@ This document defines some terms that are used across this specification.
- [Targeting Key](#targeting-key)
- [Fractional Evaluation](#fractional-evaluation)
- [Rule](#rule)
- [SDK Paradigms](#sdk-paradigms)
- [Dynamic-Context Paradigm](#dynamic-context-paradigm)
- [Static-Context Paradigm](#static-context-paradigm)
<!-- tocstop -->
@ -54,7 +71,7 @@ A developer who is setting up or configuring an application or service to use th
### Provider Author
The maintainer of an API-compliant [provider](./providers.md) which implements the necessary interfaces required for flag evaluation.
The maintainer of an API-compliant [provider](./sections/02-providers.md) which implements the necessary interfaces required for flag evaluation.
### Integration Author
@ -68,7 +85,15 @@ The maintainer of a shared library which is a dependency of many applications or
### Feature Flag SDK
The libraries used by Application Author to implement feature flags in their application or service. The interfaces defined in these libraries adhere to the Feature Flag API.
The libraries used by the Application Author to implement feature flags in their application or service. The interfaces defined in these libraries adhere to the Feature Flag API.
### Client-Side SDK
An SDK which is built for usage in client applications (e.g. single-page web applications), and typically uses the [static-context paradigm](#static-context-paradigm).
### Server-Side SDK
An SDK which is built for usage in server applications (e.g. REST services), and typically uses the [dynamic-context paradigm](#dynamic-context-paradigm).
### Feature Flag API
@ -84,9 +109,21 @@ The subset of the [Feature Flag API](#feature-flag-api) that the Application Aut
A source-of-truth for flag values and rules. Flag management systems may include SaaS feature flag vendors, custom "in-house" feature flag infrastructure, or open-source implementations.
### Client
A lightweight abstraction that provides functions to evaluate feature flags. A client is associated with a single provider, which it uses to perform evaluations.
### Provider
An SDK-compliant implementation which resolves flag values from a particular flag management system, allowing the use of the [Evaluation API](./flag-evaluation.md#flag-evaluation) as an abstraction for the system in question.
An SDK-compliant implementation which resolves flag values from a particular flag management system, allowing the use of the [Evaluation API](./sections/01-flag-evaluation.md#13-flag-evaluation) as an abstraction for the system in question.
### Provider Lifecycle
The possible states and transitions of a provider over the course of its usage, as defined by the [provider interface](./sections/02-providers.md).
### Domain
An identifier which logically binds clients with providers, allowing for multiple providers to be used simultaneously within a single application. Domain binding is dynamic; it may change over the course of an application's lifetime (i.e.: a client associated with the default provider via an unbound domain will be bound to a new provider if a provider is subsequently assigned to that domain).
### Integration
@ -96,9 +133,13 @@ An SDK-compliant secondary function that is abstracted by the Feature Flag API,
Context object for flag evaluation, which may contain information about the runtime environment, details of the transport method encapsulating the flag evaluation, the host, the client, the subject (user), etc. This data may be used as a basis for differential evaluation of feature flags based on rules that can be defined in the flag system. Context data may be provided by merging static global context, arguments to flag evaluation, and implicit language-dependant state propagation mechanisms (thread-local storage, promise chains, continuations, etc).
### Transaction Context Propagator
An SDK-compliant implementation that stores and returns transaction-specific evaluation context. A _transaction_ might be a web request or application event, which carries its contextual data in a thread or continuation storage.
### Evaluating Flag Values
The process of retrieving a feature flag value in it's entirety, including:
The process of retrieving a feature flag value in its entirety, including:
- any effects resulting from hooks
- resolving a flag value from a configured provider
@ -106,7 +147,11 @@ The process of retrieving a feature flag value in it's entirety, including:
### Resolving Flag Values
The process of a provider retrieving a feature flag value from it's particular source-of-truth.
The process of a provider retrieving a feature flag value from its particular source-of-truth.
### Tracking Event
A particular user action or application state representing a business objective or outcome, identified by a unique string, and recorded using the [tracking API](./sections/06-tracking.md).
## Flagging specifics
@ -122,9 +167,13 @@ erDiagram
Flags represent a single pivot point of logic. Flags have a type, like `string`, `boolean`, `json`, etc. Examples: `redesign_enabled` or `header-order`
### Flag Set
A collection of related [flags](#flag). This grouping helps organize feature flags based on their intended use, facilitating easier management and deployment.
### Flag Key
A string logically identifies a particular flag.
A string that logically identifies a particular flag.
### Variant
@ -150,8 +199,30 @@ A string logically identifying the subject of evaluation (end-user, service, etc
### Fractional Evaluation
Assigning flag values randomly according to a configured proportion or percentage (ie: 50/50).
Pseudorandomly resolve flag values using a context property, such as a targeting key, based on a configured proportion or percentage (ie: 50/50).
### Rule
A rule is some criteria that's used to determine which variant a particular context should be mapped to.
## SDK Paradigms
Feature flag frameworks have SDKs which operate in two distinct paradigms: those designed for use with a single user client application (e.g. mobile phones, single-page web apps), and those designed for multi-user applications, such as web server applications. Some parts of the OpenFeature specification diverge depending on the paradigm.
### Dynamic-Context Paradigm
Server-side applications typically perform flag evaluations on behalf of many users, with each request or event being associated with a particular user or client. For this reason, server frameworks typically operate similarly to this:
- the application is initialized with some static context (geography, service name, hostname, etc)
- with each request or event, relevant dynamic context (for example, user session data, unique user identifiers) is provided to flag evaluations
### Static-Context Paradigm
In contrast to server-side or other service-type applications, client side applications typically operate in the context of a single user. Most feature flagging libraries for these applications have been designed with this in mind. Frequently, client/web libraries operate similarly to this:
- an initialization occurs, which fetches evaluated flags in bulk for a given context (user)
- the evaluated flags are cached in the library
- flag evaluations take place against this cache, without a need to provide context (context was already used to evaluate flags in bulk)
- libraries provide a mechanism to update context (e.g. if a user logs in), meaning cached evaluations are no longer valid and must be re-evaluated, frequently involving a network request or I/O operation
Not all client libraries work this way, but generally, libraries that accept dynamic context per evaluation can build providers which conform to this model with relative ease, while the reverse is not true.

View File

@ -1,183 +0,0 @@
# Hooks
## Overview
`Hooks` are a mechanism whereby application developers can add arbitrary behavior to flag evaluation. They operate similarly to middleware in many web frameworks.
Hooks add their logic at any of four specific stages of flag evaluation:
- `before`, immediately before flag evaluation
- `after`, immediately after successful flag evaluation
- `error`, immediately after an unsuccessful during flag evaluation
- `finally` unconditionally after flag evaluation
![Flag evaluation life cycle](./assets/images/life-cycle.png)
Hooks can be configured to run globally (impacting all flag evaluations), per client, or per flag evaluation invocation. Some example use-cases for hook include adding additional data to the [evaluation context](./evaluation-context.md), performing validation on the received flag value, providing data to telemetric tools, and logging errors.
### Definitions
**Hook**: Application author/integrator-supplied logic that is called by the OpenFeature framework at a specific stage. **Stage**: An explicit portion of the flag evaluation lifecycle. e.g. `before` being "before the [resolution](./glossary.md#resolving-flag-values) is run. **Invocation**: A single call to evaluate a flag. `client.getBooleanValue(..)` is an invocation. **API**: The global API singleton.
### Hook context
Hook context exists to provide hooks with information about the invocation.
#### Requirement 4.1.1
> Hook context **MUST** provide: the `flag key`, `flag value type`, `evaluation context`, and the `default value`.
#### Requirement 4.1.2
> The `hook context` **SHOULD** provide: access to the `client metadata` and the `provider metadata` fields.
#### Requirement 4.1.3
> The `flag key`, `flag type`, and `default value` properties **MUST** be immutable. If the language does not support immutability, the hook **MUST NOT** modify these properties.
#### Requirement 4.1.4
> The evaluation context **MUST** be mutable only within the `before` hook.
### Hook Hints
#### Requirement 4.2.1
> `hook hints` **MUST** be a structure supports definition of arbitrary properties, with keys of type `string`, and values of type `boolean | string | number | datetime | structure`..
#### Condition 4.2.2
> The implementation language supports a mechanism for marking data as immutable.
##### Conditional Requirement 4.2.2.1
> Condition: `Hook hints` **MUST** be immutable.
##### Conditional Requirement 4.2.2.2
> Condition: The client `metadata` field in the `hook context` **MUST** be immutable.
##### Conditional Requirement 4.2.2.3
> Condition: The provider `metadata` field in the `hook context` **MUST** be immutable.
### Hook creation and parameters
#### Requirement 4.3.1
> Hooks **MUST** specify at least one stage.
#### Requirement 4.3.2
> The `before` stage **MUST** run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters and returns either an `evaluation context` or nothing.
```typescript
EvaluationContext | void before(HookContext, HookHints);
```
#### Requirement 4.3.3
> Any `evaluation context` returned from a `before` hook **MUST** be passed to subsequent `before` hooks (via `HookContext`).
#### Requirement 4.3.4
> When `before` hooks have finished executing, any resulting `evaluation context` **MUST** be merged with the invocation `evaluation context` with the invocation `evaluation context` taking precedence in the case of any conflicts.
#### Requirement 4.3.5
> The `after` stage **MUST** run after flag resolution occurs. It accepts a `hook context` (required), `flag evaluation details` (required) and `hook hints` (optional). It has no return value.
#### Requirement 4.3.6
> The `error` hook **MUST** run when errors are encountered in the `before` stage, the `after` stage or during flag resolution. It accepts `hook context` (required), `exception` representing what went wrong (required), and `hook hints` (optional). It has no return value.
#### Requirement 4.3.7
> The `finally` hook **MUST** run after the `before`, `after`, and `error` stages. It accepts a `hook context` (required) and `hook hints` (optional). There is no return value.
#### Condition 4.3.8
> `finally` is a reserved word in the language.
##### Conditional Requirement 4.3.8.1
> Instead of `finally`, `finallyAfter` **SHOULD** be used.
### Hook registration & ordering
#### Requirement 4.4.1
> The API, Client and invocation **MUST** have a method for registering hooks which accepts `flag evaluation options`
```js
OpenFeature.addHooks(new Hook1());
//...
Client client = OpenFeature.getClient();
client.addHooks(new Hook2());
`
//...
client.getValue('my-flag', 'defaultValue', new Hook3());
```
#### Requirement 4.4.2
> Hooks **MUST** be evaluated in the following order:
>
> - before: API, Client, Invocation
> - after: Invocation, Client, API
> - error (if applicable): Invocation, Client, API
> - finally: Invocation, Client, API
#### Requirement 4.4.3
> If a `finally` hook abnormally terminates, evaluation **MUST** proceed, including the execution of any remaining `finally` hooks.
In languages with try/catch semantics, this means that exceptions thrown in `finally` hooks should be caught, and not propagated up the call stack.
#### Requirement 4.4.4
> If an `error` hook abnormally terminates, evaluation **MUST** proceed, including the execution of any remaining `error` hooks.
In languages with try/catch semantics, this means that exceptions thrown in `error` hooks should be caught, and not propagated up the call stack.
#### Requirement 4.4.5
> If an error occurs in the `before` or `after` hooks, the `error` hooks **MUST** be invoked.
#### Requirement 4.4.6
> If an error occurs during the evaluation of `before` or `after` hooks, any remaining hooks in the `before` or `after` stages **MUST NOT** be invoked.
#### Requirement 4.4.7
> If an error occurs in the `before` hooks, the default value **MUST** be returned.
Before hooks can impact evaluation by various means, such as mutating the `evaluation context`. Therefore, an error in the `before` hooks is considered abnormal execution, and the default should be returned.
### [Flag evaluation options](./types.md#evaluation-options)
Usage might look something like:
```python
val = client.get_boolean_value('my-key', False, evaluation_options={
'hooks': new MyHook(),
'hook_hints': {'side-item': 'onion rings'}
})
```
See: [Flag evaluation options](./flag-evaluation.md#)
#### Requirement 4.5.1
> `Flag evaluation options` **MAY** contain `hook hints`, a map of data to be provided to hook invocations.
#### Requirement 4.5.2
> `hook hints` **MUST** be passed to each hook.
#### Requirement 4.5.3
> The hook **MUST NOT** alter the `hook hints` structure.

View File

@ -1,163 +0,0 @@
# Provider
## Overview
The `provider` API defines interfaces that Provider Authors can use to abstract a particular flag management system, thus enabling the use of the `evaluation API` by Application Authors.
Providers are the "translator" between the flag evaluation calls made in application code, and the flag management system that stores flags and in some cases evaluates flags. At a minimum, providers should implement some basic evaluation methods which return flag values of the expected type. In addition, providers may transform the [evaluation context](evaluation-context.md) appropriately in order to be used in dynamic evaluation of their associated flag management system, provide insight into why evaluation proceeded the way it did, and expose configuration options for their associated flag management system. Hypothetical provider implementations might wrap a vendor SDK, embed an REST client, or read flags from a local file.
![Provider](./assets/images/provider.png)
### Feature Provider Interface
#### Requirement 2.1
> The provider interface **MUST** define a `metadata` member or accessor, containing a `name` field or accessor of type string, which identifies the provider implementation.
```typescript
provider.getMetadata().getName(); // "my-custom-provider"
```
#### Flag Value Resolution
`Providers` are implementations of the `feature provider` interface, which may wrap vendor SDKs, REST API clients, or otherwise resolve flag values from the runtime environment.
##### Requirement 2.2
> The `feature provider` interface **MUST** define methods to resolve flag values, with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), `evaluation context` (optional), and `evaluation options` (optional), which returns a `flag resolution` structure.
```typescript
// example flag resolution function
resolveBooleanValue(flagKey, defaultValue, context, options);
```
see: [flag resolution structure](./types.md#flag-resolution), [flag value resolution](./glossary.md#flag-value-resolution)
##### Condition 2.3
> The implementing language type system differentiates between strings, numbers, booleans and structures.
###### Conditional Requirement 2.3.1
> The `feature provider` interface **MUST** define methods for typed flag resolution, including boolean, numeric, string, and structure.
```typescript
// example boolean flag value resolution
ResolutionDetails resolveBooleanValue(string flagKey, boolean defaultValue, context: EvaluationContext, options: FlagEvaluationOptions);
// example string flag value resolution
ResolutionDetails resolveStringValue(string flagKey, string defaultValue, context: EvaluationContext, options: FlagEvaluationOptions);
// example number flag value resolution
ResolutionDetails resolveNumberValue(string flagKey, number defaultValue, context: EvaluationContext, options: FlagEvaluationOptions);
// example structure flag value resolution
ResolutionDetails resolveStructureValue(string flagKey, JsonObject defaultValue, context: EvaluationContext, options: FlagEvaluationOptions);
```
##### Requirement 2.4
> In cases of normal execution, the `provider` **MUST** populate the `flag resolution` structure's `value` field with the resolved flag value.
##### Requirement 2.5
> In cases of normal execution, the `provider` **SHOULD** populate the `flag resolution` structure's `variant` field with a string identifier corresponding to the returned flag value.
For example, the flag value might be `3.14159265359`, and the variant field's value might be `"pi"`.
The value of the variant field might only be meaningful in the context of the flag management system associated with the provider. For example, the variant may be a UUID corresponding to the variant in the flag management system, or an index corresponding to the variant in the flag management system.
##### Requirement 2.6
> The `provider` **SHOULD** populate the `flag resolution` structure's `reason` field with a string indicating the semantic reason for the returned flag value.
Possible values vary by provider, but might include such values as `"TARGETING_MATCH"`, `"SPLIT"`, `"DISABLED"`, `"DEFAULT"`, `"UNKNOWN"` or `"ERROR"`.
##### Requirement 2.7
> In cases of normal execution, the `provider` **MUST NOT** populate the `flag resolution` structure's `error code` field, or otherwise must populate it with a null or falsy value.
##### Requirement 2.8
> In cases of abnormal execution, the `provider` **MUST** indicate an error using the idioms of the implementation language, with an associated error code having possible values `"PROVIDER_NOT_READY"`, `"FLAG_NOT_FOUND"`, `"PARSE_ERROR"`, `"TYPE_MISMATCH"`, or `"GENERAL"`.
The provider might throw an exception, return an error, or populate the `error code` object on the returned `flag resolution` structure to indicate a problem during flag value resolution.
##### Condition 2.9
> The implementation language supports generics (or an equivalent feature).
###### Conditional Requirement 2.9.1
> The `flag resolution` structure **SHOULD** accept a generic argument (or use an equivalent language feature) which indicates the type of the wrapped `value` field.
```typescript
// example boolean flag value resolution with generic argument
ResolutionDetails<boolean> resolveBooleanValue(string flagKey, boolean defaultValue, context: EvaluationContext, options: FlagEvaluationOptions);
// example string flag value resolution with generic argument
ResolutionDetails<string> resolveStringValue(string flagKey, string defaultValue, context: EvaluationContext, options: FlagEvaluationOptions);
// example number flag value resolution with generic argument
ResolutionDetails<number> resolveNumberValue(string flagKey, number defaultValue, context: EvaluationContext, options: FlagEvaluationOptions);
// example structure flag value resolution with generic argument
ResolutionDetails<MyStruct> resolveStructureValue(string flagKey, MyStruct defaultValue, context: EvaluationContext, options: FlagEvaluationOptions);
```
#### Context Transformation
Feature flag management systems often define structures representing arbitrary contextual data pertaining to the runtime, user, or application. The context transformer defines a simple interface to transform the OpenFeature `evaluation context` to such a structure, mapping values appropriately.
See [evaluation context](./evaluation-context.md).
##### Requirement 2.10
> The provider interface **MAY** define a `context transformer` method or function, which can be optionally implemented in order to transform the `evaluation context` prior to flag value resolution.
The OpenFeature `client` might apply the transformer function before passing the returned value (the `transformed context`) to the provider resolution methods, thus allowing the provider implementation to avoid implementing and calling such transformation logic repeatedly in flag value resolution methods.
```typescript
class MyProvider implements Provider {
//...
// implementation of context transformer
MyProviderContext transformContext(EvaluationContext context) {
return new MyProviderContext(context.email, context.ip, context.httpMethod);
}
//...
}
```
See [evaluation context](./evaluation-context.md), [flag evaluation](./flag-evaluation.md#flag-evaluation).
##### Condition 2.11
> The implementation language supports generics (or an equivalent feature).
###### Conditional Requirement 2.11.1
> If the implementation includes a `context transformer`, the provider **SHOULD** accept a generic argument (or use an equivalent language feature) indicating the type of the transformed context.
>
> If such type information is supplied, more accurate type information can be supplied in the flag resolution methods.
```typescript
// an example implementation in a language supporting interfaces, classes, and generics
// T represents a generic argument for the type of the transformed context
interface Provider<T> {
//...
// context transformer signature
T transformContext(EvaluationContext context);
//...
// flag resolution methods context parameter type corresponds to class-generic
boolean resolveBooleanValue (string flagKey, boolean defaultValue, T transformedContext, EvaluationOptions options);
//...
}
```

View File

@ -0,0 +1,532 @@
---
title: Flag Evaluation API
description: The specification that defines the developer facing feature flag evaluation API.
toc_max_heading_level: 4
---
# 1. Flag Evaluation API
[![hardening](https://img.shields.io/static/v1?label=Status&message=hardening&color=yellow)](https://github.com/open-feature/spec/tree/main/specification#hardening)
## Overview
The `evaluation API` allows for the evaluation of feature flag values, independent of any flag control plane or vendor. In the absence of a [provider](./02-providers.md) the `evaluation API` uses the "No-op provider", which simply returns the supplied default flag value.
### 1.1. API Initialization and Configuration
#### Requirement 1.1.1
> The `API`, and any state it maintains **SHOULD** exist as a global singleton, even in cases wherein multiple versions of the `API` are present at runtime.
It's important that multiple instances of the `API` not be active, so that state stored therein, such as the registered `provider`, static global `evaluation context`, and globally configured `hooks` allow the `API` to behave predictably. This can be difficult in some runtimes or languages, but implementors should make their best effort to ensure that only a single instance of the `API` is used.
### Setting a provider
#### Requirement 1.1.2.1
> The `API` **MUST** define a `provider mutator`, a function to set the default `provider`, which accepts an API-conformant `provider` implementation.
```typescript
// example provider mutator
OpenFeature.setProvider(new MyProvider());
```
The example above sets the default provider.
This provider is used if a client is not bound to a specific provider via a [domain](../glossary.md#domain).
See [provider](./02-providers.md), [creating clients](#creating-clients) for details.
#### Requirement 1.1.2.2
> The `provider mutator` function **MUST** invoke the `initialize` function on the newly registered provider before using it to resolve flag values.
Application authors can await the newly set `provider's` readiness using the `PROVIDER_READY` event.
Provider instances which are already active (because they have been bound to another `domain` or otherwise) need not be initialized again.
The `provider's` readiness state can be determined from its `status` member/accessor.
See [event handlers and initialization](./05-events.md#event-handlers-and-initialization), [provider initialization](./02-providers.md#24-initialization), [domain](../glossary.md#domain) for details.
#### Requirement 1.1.2.3
> The `provider mutator` function **MUST** invoke the `shutdown` function on the previously registered provider once it's no longer being used to resolve flag values.
When a provider is no longer in use, it should be disposed of using its `shutdown` mechanism.
Provider instances which are bound to multiple `domains` won't be shut down until the last binding is removed.
see [shutdown](./02-providers.md#25-shutdown), [setting a provider](#setting-a-provider), [domain](../glossary.md#domain) for details.
#### Requirement 1.1.2.4
> The `API` **SHOULD** provide functions to set a provider and wait for the `initialize` function to complete or abnormally terminate.
This function not only sets the provider, but ensures that the provider is ready (or in error) before returning or settling.
```java
// default provider
OpenFeatureAPI.getInstance().setProviderAndWait(myprovider); // this method blocks until the provider is ready or in error
// client uses the default provider
Client client = OpenFeatureAPI.getInstance().getClient();
// provider associated with domain-1
OpenFeatureAPI.getInstance().setProviderAndWait('domain-1', myprovider); // this method blocks until the provider is ready or in error
// client uses provider associated with the domain named 'domain-1'
Client client = OpenFeatureAPI.getInstance().getClient('domain-1');
```
Though it's possible to use [events](./05-events.md) to await provider readiness, such functions can make things simpler for `application authors` and `integrators`.
Implementations indicate an error in a manner idiomatic to the language in use (returning an error, throwing an exception, etc).
#### Requirement 1.1.3
> The `API` **MUST** provide a function to bind a given `provider` to one or more clients using a `domain`. If the domain already has a bound provider, it is overwritten with the new mapping.
```java
OpenFeature.setProvider("domain-1", new MyProvider());
```
Clients can be associated with a particular provider by supplying a matching `domain` when the provider is set.
See [creating clients](#creating-clients), [domain](../glossary.md#domain) for details.
#### Requirement 1.1.4
> The `API` **MUST** provide a function to add `hooks` which accepts one or more API-conformant `hooks`, and appends them to the collection of any previously added hooks. When new hooks are added, previously added hooks are not removed.
```typescript
// example hook attachment
OpenFeature.addHooks([new MyHook()]);
```
See [hooks](./04-hooks.md) for details.
#### Requirement 1.1.5
> The `API` **MUST** provide a function for retrieving the metadata field of the configured `provider`.
```typescript
// example provider accessor
OpenFeature.getProviderMetadata();
```
It's possible to access provider metadata using a `domain`.
If a provider has not been registered under the requested domain, the default provider metadata is returned.
```typescript
// example provider accessor
OpenFeature.getProviderMetadata("domain-1");
```
See [provider](./02-providers.md), [domain](../glossary.md#domain) for details.
### Creating clients
#### Requirement 1.1.6
> The `API` **MUST** provide a function for creating a `client` which accepts the following options:
>
> - domain (optional): A logical string identifier for binding a client to a provider.
```java
// example client creation and retrieval
OpenFeature.getClient();
```
It's possible to create a client that is associated with a `domain`.
The client will use a provider in the same `domain` if one exists, otherwise, the default provider is used.
```java
// example client creation and retrieval using a domain
OpenFeature.getClient("domain-1");
```
See [setting a provider](#setting-a-provider), [domain](../glossary.md#domain) for details.
#### Requirement 1.1.7
> The client creation function **MUST NOT** throw, or otherwise abnormally terminate.
Clients may be created in critical code paths, and even per-request in server-side HTTP contexts. Therefore, in keeping with the principle that OpenFeature should never cause abnormal execution of the first party application, this function should never throw. Abnormal execution in initialization should instead occur during provider registration.
### 1.2. Client Usage
#### Requirement 1.2.1
> The client **MUST** provide a method to add `hooks` which accepts one or more API-conformant `hooks`, and appends them to the collection of any previously added hooks. When new hooks are added, previously added hooks are not removed.
```typescript
// example hook attachment
client.addHooks([new MyHook()]);
```
See [hooks](./04-hooks.md) for details.
#### Requirement 1.2.2
> The client interface **MUST** define a `metadata` member or accessor, containing an immutable `domain` field or accessor of type string, which corresponds to the `domain` value supplied during client creation.
```typescript
client.getMetadata().getDomain(); // "domain-1"
```
In previous drafts, this property was called `name`.
For backwards compatibility, implementations should consider `name` an alias to `domain`.
### 1.3. Flag Evaluation
[![hardening](https://img.shields.io/static/v1?label=Status&message=hardening&color=yellow)](https://github.com/open-feature/spec/tree/main/specification#hardening)
#### Condition 1.3.1
> The implementation uses the dynamic-context paradigm.
see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm)
##### Conditional Requirement 1.3.1.1
> The `client` **MUST** provide methods for typed flag evaluation, including boolean, numeric, string, and structure, with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), `evaluation context` (optional), and `evaluation options` (optional), which returns the flag value.
```typescript
// example boolean flag evaluation
boolean myBool = client.getBooleanValue('bool-flag', false);
// example overloaded string flag evaluation with optional params
string myString = client.getStringValue('string-flag', 'N/A', evaluationContext, options);
// example number flag evaluation
number myNumber = client.getNumberValue('number-flag', 75);
// example overloaded structure flag evaluation with optional params
MyStruct myStruct = client.getObjectValue<MyStruct>('structured-flag', { text: 'N/A', percentage: 75 }, evaluationContext, options);
```
See [evaluation context](./03-evaluation-context.md) for details.
#### Condition 1.3.2
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
> The implementation uses the static-context paradigm.
see: [static-context paradigm](../glossary.md#static-context-paradigm)
##### Conditional Requirement 1.3.2.1
> The `client` **MUST** provide methods for typed flag evaluation, including boolean, numeric, string, and structure, with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), and `evaluation options` (optional), which returns the flag value.
```typescript
// example boolean flag evaluation
boolean myBool = client.getBooleanValue('bool-flag', false);
// example overloaded string flag evaluation with optional params
string myString = client.getStringValue('string-flag', 'N/A', options);
// example number flag evaluation
number myNumber = client.getNumberValue('number-flag', 75);
// example overloaded structure flag evaluation with optional params
MyStruct myStruct = client.getObjectValue<MyStruct>('structured-flag', { text: 'N/A', percentage: 75 }, options);
```
#### Condition 1.3.3
> The implementation language differentiates between floating-point numbers and integers.
##### Conditional Requirement 1.3.3.1
> The client **SHOULD** provide functions for floating-point numbers and integers, consistent with language idioms.
```java
int getIntValue(String flag, int defaultValue);
long getFloatValue(String flag, long defaultValue);
```
See [types](../types.md) for details.
#### Requirement 1.3.4
> The `client` **SHOULD** guarantee the returned value of any typed flag evaluation method is of the expected type. If the value returned by the underlying provider implementation does not match the expected type, it's to be considered abnormal execution, and the supplied `default value` should be returned.
### 1.4. Detailed Flag Evaluation
[![hardening](https://img.shields.io/static/v1?label=Status&message=hardening&color=yellow)](https://github.com/open-feature/spec/tree/main/specification#hardening)
The _detailed evaluation_ functions behave similarly to the standard flag evaluation functions, but provide additional metadata useful for telemetry, troubleshooting, debugging, and advanced integrations.
> [!NOTE]
> Metadata included in the `evaluation details` should be considered "best effort", since not all providers will be able to provide these details (such as the reason a flag resolved to a particular value).
see: [evaluation details](../types.md#evaluation-details) type
#### Condition 1.4.1
> The implementation uses the dynamic-context paradigm.
see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm)
##### Conditional Requirement 1.4.1.1
> The `client` **MUST** provide methods for detailed flag value evaluation with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), `evaluation context` (optional), and `evaluation options` (optional), which returns an `evaluation details` structure.
```typescript
// example detailed boolean flag evaluation
FlagEvaluationDetails<boolean> myBoolDetails = client.getBooleanDetails('bool-flag', false);
// example detailed string flag evaluation
FlagEvaluationDetails<string> myStringDetails = client.getStringDetails('string-flag', 'N/A', evaluationContext, options);
// example detailed number flag evaluation
FlagEvaluationDetails<number> myNumberDetails = client.getNumberDetails('number-flag', 75);
// example detailed structure flag evaluation
FlagEvaluationDetails<MyStruct> myStructDetails = client.getObjectDetails<MyStruct>('structured-flag', { text: 'N/A', percentage: 75 }, evaluationContext, options);
```
#### Condition 1.4.2
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
> The implementation uses the static-context paradigm.
see: [static-context paradigm](../glossary.md#static-context-paradigm)
##### Conditional Requirement 1.4.2.1
> The `client` **MUST** provide methods for detailed flag value evaluation with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), and `evaluation options` (optional), which returns an `evaluation details` structure.
```typescript
// example detailed boolean flag evaluation
FlagEvaluationDetails<boolean> myBoolDetails = client.getBooleanDetails('bool-flag', false);
// example detailed string flag evaluation
FlagEvaluationDetails<string> myStringDetails = client.getStringDetails('string-flag', 'N/A', options);
// example detailed number flag evaluation
FlagEvaluationDetails<number> myNumberDetails = client.getNumberDetails('number-flag', 75);
// example detailed structure flag evaluation
FlagEvaluationDetails<MyStruct> myStructDetails = client.getObjectDetails<MyStruct>('structured-flag', { text: 'N/A', percentage: 75 }, options);
```
#### Requirement 1.4.3
> The `evaluation details` structure's `value` field **MUST** contain the evaluated flag value.
#### Condition 1.4.4
> The language supports generics (or an equivalent feature).
##### Conditional Requirement 1.4.4.1
> The `evaluation details` structure **SHOULD** accept a generic argument (or use an equivalent language feature) which indicates the type of the wrapped `value` field.
#### Requirement 1.4.5
> The `evaluation details` structure's `flag key` field **MUST** contain the `flag key` argument passed to the detailed flag evaluation method.
#### Requirement 1.4.6
> In cases of normal execution, the `evaluation details` structure's `variant` field **MUST** contain the value of the `variant` field in the `flag resolution` structure returned by the configured `provider`, if the field is set.
#### Requirement 1.4.7
> In cases of normal execution, the `evaluation details` structure's `reason` field **MUST** contain the value of the `reason` field in the `flag resolution` structure returned by the configured `provider`, if the field is set.
#### Requirement 1.4.8
> In cases of abnormal execution, the `evaluation details` structure's `error code` field **MUST** contain an `error code`.
See [error code](../types.md#error-code) for details.
#### Requirement 1.4.9
> In cases of abnormal execution (network failure, unhandled error, etc) the `reason` field in the `evaluation details` **SHOULD** indicate an error.
#### Requirement 1.4.10
> Methods, functions, or operations on the client **MUST NOT** throw exceptions, or otherwise abnormally terminate. Flag evaluation calls must always return the `default value` in the event of abnormal execution. Exceptions include functions or methods for the purposes for configuration or setup.
Configuration code includes code to set the provider, instantiate providers, and configure the global API object.
#### Requirement 1.4.11
> Methods, functions, or operations on the client **SHOULD NOT** write log messages.
The client methods (particularly the evaluation methods) run in hot code paths.
Logging (even at error level) can cause a huge volume of log entries.
For example, in a circumstance in which an application expecting a particular flag to exist is deployed in advance of that flag's being defined in the management system, logs can become inundated with `FLAG_NOT_FOUND` messages and related stack traces.
Logging in these code paths is highly discouraged.
Application authors can attach a [logging hook](../appendix-a-included-utilities.md#logging-hook) or author their own custom logging hook(s) to help with debugging or satisfy their particular logging needs.
Logging is encouraged in functions to do with configuration, initialization, shutdown, etc.
#### Requirement 1.4.12
> The `client` **SHOULD** provide asynchronous or non-blocking mechanisms for flag evaluation.
It's recommended to provide non-blocking mechanisms for flag evaluation, particularly in languages or environments wherein there's a single thread of execution.
#### Requirement 1.4.13
> In cases of abnormal execution, the `evaluation details` structure's `error message` field **MAY** contain a string containing additional details about the nature of the error.
#### Requirement 1.4.14
> If the `flag metadata` field in the `flag resolution` structure returned by the configured `provider` is set, the `evaluation details` structure's `flag metadata` field **MUST** contain that value. Otherwise, it **MUST** contain an empty record.
This `flag metadata` field is intended as a mechanism for providers to surface additional information about a feature flag (or its evaluation) beyond what is defined within the OpenFeature spec itself. The primary consumer of this information is a provider-specific hook.
#### Condition 1.4.15
> The implementation language supports a mechanism for marking data as immutable.
##### Conditional Requirement 1.4.14.1
> Condition: `Flag metadata` **MUST** be immutable.
### Evaluation Options
#### Requirement 1.5.1
> The `evaluation options` structure's `hooks` field denotes an ordered collection of hooks that the client **MUST** execute for the respective flag evaluation, in addition to those already configured.
See [hooks](./04-hooks.md) for details.
### 1.6. Shutdown
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
The API's `shutdown` function defines a means of graceful shutdown, calling the `shutdown` function on all providers, allowing them to flush telemetry, clean up connections, and release any relevant resources.
It also provides a means of resetting the API object to its default state, removing all hooks, event handlers, providers, and setting a "No-op provider"; this is useful for testing purposes.
It's recommended that application-authors call this function on application shutdown, and after the completion of test suites which make use of the SDK.
#### Requirement 1.6.1
> The API **MUST** define a function to propagate a shutdown request to all providers.
The global API object defines a `shutdown` function, which will call the respective `shutdown` function on all providers.
Alternatively, implementations might leverage language idioms such as auto-disposable interfaces or some means of cancellation signal propagation to allow for graceful shutdown.
This shutdown function unconditionally calls the shutdown function on all registered providers, regardless of their state.
see: [`shutdown`](./02-providers.md#25-shutdown)
#### Requirement 1.6.2
> The API's `shutdown` function **MUST** reset all state of the API, removing all hooks, event handlers, and providers.
After shutting down all providers, the `shutdown` function resets the state of the API.
This is especially useful for testing purposes to restore the API to a known state.
### 1.7. Provider Lifecycle Management
The implementation maintains an internal representation of the state of configured providers, tracking the lifecycle of each provider.
This state of the provider is exposed on associated `clients`.
The diagram below illustrates the possible states and transitions of the `state` field for a provider during the provider lifecycle.
```mermaid
---
title: Provider lifecycle
---
stateDiagram-v2
direction LR
[*] --> NOT_READY
NOT_READY --> READY:initialize()
NOT_READY --> ERROR:initialize()
NOT_READY --> FATAL:initialize()
FATAL --> [*]
READY --> ERROR:#ast;
ERROR --> READY:#ast;
READY --> STALE:#ast;
STALE --> READY:#ast;
STALE --> ERROR:#ast;
READY --> NOT_READY:shutdown()
STALE --> NOT_READY:shutdown()
ERROR --> NOT_READY:shutdown()
READY --> RECONCILING:::client:setContext()
RECONCILING:::client --> READY
RECONCILING:::client --> ERROR
classDef client fill:#888
```
\* transitions occurring when associated events are spontaneously emitted from the provider
<span style={{ color: '#888' }}></span> only defined in static-context (client-side) paradigm
> [!NOTE]
> Only SDKs implementing the [static context (client-side) paradigm](../glossary.md#static-context-paradigm) define `RECONCILING` to facilitate [context reconciliation](./02-providers.md#26-provider-context-reconciliation).
#### Requirement 1.7.1
> The `client` **MUST** define a `provider status` accessor which indicates the readiness of the associated provider, with possible values `NOT_READY`, `READY`, `STALE`, `ERROR`, or `FATAL`.
The SDK at all times maintains an up-to-date state corresponding to the success/failure of the last lifecycle method (`initialize`, `shutdown`, `on context change`) or emitted event.
see [provider status](../types.md#provider-status)
#### Condition 1.7.2
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
> The implementation uses the static-context paradigm.
see: [static-context paradigm](../glossary.md#static-context-paradigm)
##### Conditional Requirement 1.7.2.1
> In addition to `NOT_READY`, `READY`, `STALE`, or `ERROR`, the `provider status` accessor must support possible value `RECONCILING`.
In the static context paradigm, the implementation must define a `provider status` indicating that a provider is reconciling its internal state due to a context change.
#### Requirement 1.7.3
> The client's `provider status` accessor **MUST** indicate `READY` if the `initialize` function of the associated provider terminates normally.
Once the provider has initialized, the `provider status` should indicate the provider is ready to be used to evaluate flags.
#### Requirement 1.7.4
> The client's `provider status` accessor **MUST** indicate `ERROR` if the `initialize` function of the associated provider terminates abnormally.
If the provider has failed to initialize, the `provider status` should indicate the provider is in an error state.
#### Requirement 1.7.5
> The client's `provider status` accessor **MUST** indicate `FATAL` if the `initialize` function of the associated provider terminates abnormally and indicates `error code` `PROVIDER_FATAL`.
If the provider has failed to initialize, the `provider status` should indicate the provider is in an error state.
#### Requirement 1.7.6
> The client **MUST** default, run error hooks, and indicate an error if flag resolution is attempted while the provider is in `NOT_READY`.
The client defaults and returns the `PROVIDER_NOT_READY` `error code` if evaluation is attempted before the provider is initialized (the provider is still in a `NOT_READY` state).
The SDK avoids calling the provider's resolver functions entirely ("short-circuits") if the provider is in this state.
see: [error codes](../types.md#error-code), [flag value resolution](./02-providers.md#22-flag-value-resolution)
#### Requirement 1.7.7
> The client **MUST** default, run error hooks, and indicate an error if flag resolution is attempted while the provider is in `FATAL`.
The client defaults and returns the `PROVIDER_FATAL` `error code` if evaluation is attempted after the provider has transitioned to an irrecoverable error state.
The SDK avoids calling the provider's resolver functions entirely ("short-circuits") if the provider is in this state.
see: [error codes](../types.md#error-code), [flag value resolution](./02-providers.md#22-flag-value-resolution)
#### Requirement 1.7.8
> Implementations **SHOULD** propagate the `error code` returned from any provider lifecycle methods.
The SDK ensures that if the provider's lifecycle methods terminate with an `error code`, that error code is included in any associated error events and returned/thrown errors/exceptions.
see: [error codes](../types.md#error-code)
#### Requirement 1.7.9
> The client's `provider status` accessor **MUST** indicate `NOT_READY` once the `shutdown` function of the associated provider terminates.
Regardless of the success of the provider's `shutdown` function, the `provider status` should convey the provider is no longer ready to use once the shutdown function terminates.

View File

@ -0,0 +1,307 @@
---
title: Provider
description: The specification that defines the interfaces, behaviors and responsibilities of providers.
toc_max_heading_level: 4
---
# 2. Provider
[![hardening](https://img.shields.io/static/v1?label=Status&message=hardening&color=yellow)](https://github.com/open-feature/spec/tree/main/specification#hardening)
## Overview
The `provider` API defines interfaces that Provider Authors can use to abstract a particular flag management system, thus enabling the use of the `evaluation API` by Application Authors.
Providers are the "translator" between the flag evaluation calls made in application code, and the flag management system that stores flags and in some cases evaluates flags. At a minimum, providers should implement some basic evaluation methods which return flag values of the expected type. In addition, providers may transform the [evaluation context](./03-evaluation-context.md) appropriately in order to be used in dynamic evaluation of their associated flag management system, provide insight into why evaluation proceeded the way it did, and expose configuration options for their associated flag management system. Hypothetical provider implementations might wrap a vendor SDK, embed an REST client, or read flags from a local file.
```mermaid
flowchart LR
A[Application] --> API(Evaluation API)
API --> P[Provider]
P --> FMS[(Flag Management System)]
```
### 2.1. Feature Provider Interface
#### Requirement 2.1.1
> The provider interface **MUST** define a `metadata` member or accessor, containing a `name` field or accessor of type string, which identifies the provider implementation.
```typescript
provider.getMetadata().getName(); // "my-custom-provider"
```
### 2.2 Flag Value Resolution
`Providers` are implementations of the `feature provider` interface, which may wrap vendor SDKs, REST API clients, or otherwise resolve flag values from the runtime environment.
#### Requirement 2.2.1
> The `feature provider` interface **MUST** define methods to resolve flag values, with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required) and `evaluation context` (optional), which returns a `resolution details` structure.
```typescript
// example flag resolution function
resolveBooleanValue(flagKey, defaultValue, context);
```
see: [flag resolution structure](../types.md#resolution-details), [flag value resolution](../glossary.md#resolving-flag-values)
#### Condition 2.2.2
> The implementing language type system differentiates between strings, numbers, booleans and structures.
##### Conditional Requirement 2.2.2.1
> The `feature provider` interface **MUST** define methods for typed flag resolution, including boolean, numeric, string, and structure.
```typescript
// example boolean flag value resolution
ResolutionDetails resolveBooleanValue(string flagKey, boolean defaultValue, context: EvaluationContext);
// example string flag value resolution
ResolutionDetails resolveStringValue(string flagKey, string defaultValue, context: EvaluationContext);
// example number flag value resolution
ResolutionDetails resolveNumberValue(string flagKey, number defaultValue, context: EvaluationContext);
// example structure flag value resolution
ResolutionDetails resolveStructureValue(string flagKey, JsonObject defaultValue, context: EvaluationContext);
```
#### Requirement 2.2.3
> In cases of normal execution, the `provider` **MUST** populate the `resolution details` structure's `value` field with the resolved flag value.
#### Requirement 2.2.4
> In cases of normal execution, the `provider` **SHOULD** populate the `resolution details` structure's `variant` field with a string identifier corresponding to the returned flag value.
For example, the flag value might be `3.14159265359`, and the variant field's value might be `"pi"`.
The value of the variant field might only be meaningful in the context of the flag management system associated with the provider. For example, the variant may be a UUID corresponding to the variant in the flag management system, or an index corresponding to the variant in the flag management system.
#### Requirement 2.2.5
> The `provider` **SHOULD** populate the `resolution details` structure's `reason` field with `"STATIC"`, `"DEFAULT",` `"TARGETING_MATCH"`, `"SPLIT"`, `"CACHED"`, `"DISABLED"`, `"UNKNOWN"`, `"STALE"`, `"ERROR"` or some other string indicating the semantic reason for the returned flag value.
As indicated in the definition of the [`resolution details`](../types.md#resolution-details) structure, the `reason` should be a string. This allows providers to reflect accurately why a flag was resolved to a particular value.
#### Requirement 2.2.6
> In cases of normal execution, the `provider` **MUST NOT** populate the `resolution details` structure's `error code` field, or otherwise must populate it with a null or falsy value.
#### Requirement 2.2.7
> In cases of abnormal execution, the `provider` **MUST** indicate an error using the idioms of the implementation language, with an associated `error code` and optional associated `error message`.
The provider might throw an exception, return an error, or populate the `error code` object on the returned `resolution details` structure to indicate a problem during flag value resolution.
See [error code](../types.md#error-code) for details.
```typescript
// example throwing an exception with an error code and optional error message.
throw new ProviderError(ErrorCode.INVALID_CONTEXT, "The 'foo' attribute must be a string.");
```
#### Condition 2.2.8
> The implementation language supports generics (or an equivalent feature).
##### Conditional Requirement 2.2.8.1
> The `resolution details` structure **SHOULD** accept a generic argument (or use an equivalent language feature) which indicates the type of the wrapped `value` field.
```typescript
// example boolean flag value resolution with generic argument
ResolutionDetails<boolean> resolveBooleanValue(string flagKey, boolean defaultValue, context: EvaluationContext);
// example string flag value resolution with generic argument
ResolutionDetails<string> resolveStringValue(string flagKey, string defaultValue, context: EvaluationContext);
// example number flag value resolution with generic argument
ResolutionDetails<number> resolveNumberValue(string flagKey, number defaultValue, context: EvaluationContext);
// example structure flag value resolution with generic argument
ResolutionDetails<MyStruct> resolveStructureValue(string flagKey, MyStruct defaultValue, context: EvaluationContext);
```
#### Requirement 2.2.9
> The `provider` **SHOULD** populate the `resolution details` structure's `flag metadata` field.
#### Requirement 2.2.10
> `flag metadata` **MUST** be a structure supporting the definition of arbitrary properties, with keys of type `string`, and values of type `boolean | string | number`.
### 2.3. Provider hooks
A `provider hook` exposes a mechanism for `provider authors` to register [`hooks`](./04-hooks.md) to tap into various stages of the flag evaluation lifecycle. These hooks can be used to perform side effects and mutate the context for purposes of the provider. Provider hooks are not configured or controlled by the `application author`.
#### Requirement 2.3.1
> The provider interface **MUST** define a `provider hook` mechanism which can be optionally implemented in order to add `hook` instances to the evaluation life-cycle.
```java
class MyProvider implements Provider {
//...
readonly hooks: Hook[] = [new MyProviderHook()];
// ..or alternatively..
getProviderHooks(): Hook[] {
return [new MyProviderHook()];
}
//...
}
```
#### Requirement 2.3.2
> In cases of normal execution, the `provider` **MUST NOT** populate the `resolution details` structure's `error message` field, or otherwise must populate it with a null or falsy value.
#### Requirement 2.3.3
> In cases of abnormal execution, the `resolution details` structure's `error message` field **MAY** contain a string containing additional detail about the nature of the error.
### 2.4 Initialization
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
#### Requirement 2.4.1
> The `provider` **MAY** define an initialization function which accepts the global `evaluation context` as an argument and performs initialization logic relevant to the provider.
Many feature flag frameworks or SDKs require some initialization before they can be used.
They might require the completion of an HTTP request, establishing persistent connections, or starting timers or worker threads.
The initialization function is an ideal place for such logic.
```java
// MyProvider implementation of the initialize function defined in Provider
class MyProvider implements Provider {
//...
// the global context is passed to the initialization function
void initialize(EvaluationContext initialContext) {
/*
A hypothetical initialization function: make an initial call doing some bulk initial evaluation, start a worker to do periodic updates
*/
this.flagCache = this.restClient.bulkEvaluate(initialContext);
this.startPolling();
}
//...
}
```
#### Condition 2.4.2
> The provider defines an `initialize` function.
##### Conditional Requirement 2.4.2.1
> If the provider's `initialize` function fails to render the provider ready to evaluate flags, it **SHOULD** abnormally terminate.
If a provider is unable to start up correctly, it should indicate abnormal execution by throwing an exception, returning an error, or otherwise indicating so by means idiomatic to the implementation language.
If the error is irrecoverable (perhaps due to bad credentials or invalid configuration) the `PROVIDER_FATAL` error code should be used.
see: [error codes](../types.md#error-code)
### 2.5. Shutdown
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
#### Requirement 2.5.1
> The provider **MAY** define a mechanism to gracefully shutdown and dispose of resources.
```java
// MyProvider implementation of the dispose function defined in Provider
class MyProvider implements Provider, AutoDisposable {
//...
void dispose() {
// close connections, terminate threads or timers, etc...
}
```
#### Requirement 2.5.2
> After a provider's `shutdown` function has terminated, the provider **SHOULD** revert to its uninitialized state.
If a provider requires initialization, once it's shut down, it must transition to its uninitialized state.
Some providers may allow reinitialization from this state.
Providers not requiring initialization are assumed to be ready at all times.
Providers in the process of initializing abort initialization if shutdown is called while they are still starting up.
see: [initialization](#24-initialization)
#### Requirement 2.5.3
> A Provider's `shutdown` function **SHOULD** be idempotent.
If a provider's `shutdown` function has been called, subsequent calls (without an intervening call to `initialize`) should have no effect.
see: [initialization](#24-initialization)
### 2.6. Provider context reconciliation
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
Static-context focused providers may need a mechanism to understand when their cache of evaluated flags must be invalidated or updated. An `on context changed` function can be defined which performs whatever operations are needed to reconcile the evaluated flags with the new context.
#### Requirement 2.6.1
> The provider **MAY** define an `on context changed` function, which takes an argument for the previous context and the newly set context, in order to respond to an evaluation context change.
Especially in static-context implementations, providers and underlying SDKs may maintain state for a particular context.
The `on context changed` function provides a mechanism to update this state, often by re-evaluating flags in bulk with respect to the new context.
```java
// MyProvider implementation of the onContextChanged function defined in Provider
class MyProvider implements Provider {
//...
onContextChanged(EvaluationContext oldContext, EvaluationContext newContext): void {
// update context-sensitive cached flags, or otherwise react to the change in the global context
}
//...
}
```
Providers may maintain remote connections, timers, threads or other constructs that need to be appropriately disposed of.
Provider authors may implement a `shutdown` function to perform relevant clean-up actions.
Alternatively, implementations might leverage language idioms such as auto-disposable interfaces or some means of cancellation signal propagation to allow for graceful shutdown.
### 2.7. Tracking Support
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
Some flag management systems support tracking functionality, which can be used to associate feature flag evaluations with subsequent user actions or application state.
See [tracking](./06-tracking.md).
#### Condition 2.7.1
> The `provider` **MAY** define a function for tracking the occurrence of a particular user action or application state, with parameters `tracking event name` (string, required), `evaluation context` (optional) and `tracking event details` (optional) which returns nothing.
```java
class MyProvider implements Tracking {
//...
/**
* Record a tracking event.
*/
public void track(String trackingEventName, EvaluationContext context, TrackingEventDetails details) {
// perform side effects to record the event
}
//...
}
```
The track function is a void function (function returning nothing).
The track function performs side effects required to record the `tracking event` in question, which may include network activity or other I/O; this I/O should not block the function call.
Providers should be careful to complete any communication or flush any relevant uncommitted tracking data before they shut down.
See [shutdown](#25-shutdown).

View File

@ -0,0 +1,219 @@
---
title: Evaluation Context
description: The specification that defines the structure and expectations of evaluation context.
toc_max_heading_level: 4
---
# 3. Evaluation Context
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
## Overview
The `evaluation context` provides ambient information for the purposes of flag evaluation. Contextual data may be used as the basis for targeting, including rule-based evaluation, overrides for specific subjects, or fractional flag evaluation.
The context might contain information about the end-user, the application, the host, or any other ambient data that might be useful in flag evaluation. For example, a flag system might define rules that return a specific value based on the user's email address, locale, or the time of day. The context provides this information. The context can be optionally provided at evaluation, and mutated in [before hooks](./04-hooks.md).
### 3.1 Fields
> [!NOTE]
> Field casing is not specified and should be chosen in accordance with language idioms.
see: [types](../types.md)
#### Requirement 3.1.1
> The `evaluation context` structure **MUST** define an optional `targeting key` field of type string, identifying the subject of the flag evaluation.
The targeting key uniquely identifies the subject (end-user, or client service) of a flag evaluation. Providers may require this field for fractional flag evaluation, rules, or overrides targeting specific users. Such providers may behave unpredictably if a targeting key is not specified at flag resolution.
#### Requirement 3.1.2
> The evaluation context **MUST** support the inclusion of custom fields, having keys of type `string`, and values of type `boolean | string | number | datetime | structure`.
see: [structure](../types.md#structure), [datetime](../types.md#datetime)
#### Requirement 3.1.3
> The evaluation context **MUST** support fetching the custom fields by key and also fetching all key value pairs.
#### Requirement 3.1.4
> The evaluation context fields **MUST** have a unique key.
The key uniquely identifies a field in the `evaluation context` and it should be unique across all types to avoid any collision when marshalling the `evaluation context` by the provider.
### 3.2 Context levels and merging
#### Condition 3.2.1
> The implementation uses the dynamic-context paradigm.
see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm)
##### Conditional Requirement 3.2.1.1
> The API, Client and invocation **MUST** have a method for supplying `evaluation context`.
API (global) `evaluation context` can be used to supply static data to flag evaluation, such as an application identifier, compute region, or hostname. Client and invocation `evaluation context` are ideal for dynamic data, such as end-user attributes.
#### Condition 3.2.2
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
> The implementation uses the static-context paradigm.
see: [static-context paradigm](../glossary.md#static-context-paradigm)
##### Conditional Requirement 3.2.2.1
> The API **MUST** have a method for setting the global `evaluation context`.
API (global) `evaluation context` can be used to supply data to flag evaluation, such as (but not limited to) user name, email, or user organization membership changes.
##### Conditional Requirement 3.2.2.2
> The Client and invocation **MUST NOT** have a method for supplying `evaluation context`.
In the static-context paradigm, context is global. The client and invocation cannot supply evaluation context.
##### Conditional Requirement 3.2.2.3
> The API **MUST** have a method for setting `evaluation context` for a `domain`.
In the static-context paradigm, provider specific context can be set using the associated `domain`.
The global context is used if there is no matching provider specific context.
See [setting a provider](./01-flag-evaluation.md#setting-a-provider), [domain](../glossary.md#domain) for details.
##### Conditional Requirement 3.2.2.4
> The API **MUST** have a mechanism to manage `evaluation context` for an associated `domain`.
In the static-context paradigm, it's possible to create and remove provider-specific context.
See [setting a provider](./01-flag-evaluation.md#setting-a-provider), [domain](../glossary.md#domain) for details.
#### Requirement 3.2.3
> Evaluation context **MUST** be merged in the order: API (global; lowest precedence) -> transaction -> client -> invocation -> before hooks (highest precedence), with duplicate values being overwritten.
Any fields defined in the transaction `evaluation context` will overwrite duplicate fields defined in the global `evaluation context`, any fields defined in the client `evaluation context` will overwrite duplicate fields defined in the transaction `evaluation context`, and fields defined in the invocation `evaluation context` will overwrite duplicate fields defined globally or on the client. Any resulting `evaluation context` from a [before hook](./04-hooks.md#requirement-434) will overwrite duplicate fields defined globally, on the client, or in the invocation.
```mermaid
flowchart LR
global("API (global)")
transaction("Transaction")
client("Client")
invocation("Invocation")
hook("Before Hooks")
global --> transaction
transaction --> client
client --> invocation
invocation --> hook
```
This describes the precedence of all `evaluation context` variants. Depending on the `paradigm`, not all variants might be available in an `SDK` implementation.
#### Condition 3.2.4
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
> The implementation uses the static-context paradigm.
see: [static-context paradigm](../glossary.md#static-context-paradigm)
##### Conditional Requirement 3.2.4.1
> When the global `evaluation context` is set, the `on context changed` function **MUST** run.
The SDK implementation must run the `on context changed` function on all registered providers that use the global `evaluation context` whenever it is mutated.
##### Conditional Requirement 3.2.4.2
> When the `evaluation context` for a specific provider is set, the `on context changed` function **MUST** only run on the associated provider.
The SDK implementation must run the `on context changed` function only on the provider that is scoped to the mutated `evaluation context`.
### 3.3 Context Propagation
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
`Transaction context` is a container for transaction-specific `evaluation context` (e.g. user id, user agent, IP).
Transaction context can be set where specific data is available (e.g. an auth service or request handler) and by using the `transaction context propagator` it will automatically be applied to all flag evaluations within a transaction (e.g. a request or thread).
The following shows a possible TypeScript implementation using [AsyncLocalStorage (async_hooks)](https://nodejs.org/api/async_context.html):
```typescript
export class AsyncLocalStorageTransactionContext implements TransactionContextPropagator {
private asyncLocalStorage = new AsyncLocalStorage<EvaluationContext>();
getTransactionContext(): EvaluationContext {
return this.asyncLocalStorage.getStore() ?? {};
}
setTransactionContext(context: EvaluationContext, callback: () => void): void {
this.asyncLocalStorage.run(context, callback);
}
}
/**
* This example is based on an express middleware.
*/
app.use((req: Request, res: Response, next: NextFunction) => {
const ip = res.headers.get("X-Forwarded-For")
OpenFeature.setTransactionContext({ targetingKey: req.user.id, ipAddress: ip }, () => {
// The transaction context is used in any flag evaluation throughout the whole call chain of next
next();
});
})
```
#### Condition 3.3.1
> The implementation uses the dynamic-context paradigm.
see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm)
##### Conditional Requirement 3.3.1.1
> The API **SHOULD** have a method for setting a `transaction context propagator`.
If there already is a `transaction context propagator`, it is replaced with the new one.
#### Condition 3.3.1.2
> The SDK implements context propagation.
A language may not have any applicable way of implementing `transaction context propagation` so the language SDK might not implement context propagation.
##### Conditional Requirement 3.3.1.2.1
> The API **MUST** have a method for setting the `evaluation context` of the `transaction context propagator` for the current transaction.
If a `transaction context propagator` is set, the SDK will call the [method defined in 3.3.1.3](#conditional-requirement-33122) with this `evaluation context` and so this `evaluation context` will be available during the current transaction.
If no `transaction context propagator` is set, this `evaluation context` is not used for evaluations.
This method then can be used for example in a request handler to add request-specific information to the `evaluation context`.
##### Conditional Requirement 3.3.1.2.2
> A `transaction context propagator` **MUST** have a method for setting the `evaluation context` of the current transaction.
A `transaction context propagator` is responsible for persisting context for the duration of a single transaction.
Typically, a transaction context propagator will propagate the context using a language-specific carrier such as [ThreadLocal (Java)](https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html), [async hooks (Node.js)](https://nodejs.org/api/async_hooks.html), [Context (Go)](https://pkg.go.dev/context) or another similar mechanism.
##### Conditional Requirement 3.3.1.2.3
> A `transaction context propagator` **MUST** have a method for getting the `evaluation context` of the current transaction.
This will be used by the SDK implementation when merging the context for evaluating a feature flag.
#### Condition 3.3.2
> The implementation uses the static-context paradigm.
see: [static-context paradigm](../glossary.md#static-context-paradigm)
##### Conditional Requirement 3.3.2.1
> The API **MUST NOT** have a method for setting a `transaction context propagator`.
In the static-context paradigm, context is global, so there must not be different contexts between transactions.

View File

@ -0,0 +1,409 @@
---
title: Hooks
description: The specification that defines the expectations and life cycle of hooks.
toc_max_heading_level: 4
---
# 4. Hooks
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
## Overview
`Hooks` are a mechanism whereby application developers can add arbitrary behavior to flag evaluation. They operate similarly to middleware in many web frameworks.
Hooks add their logic at any of four specific stages of flag evaluation:
- `before`, immediately before flag evaluation
- `after`, immediately after successful flag evaluation
- `error`, immediately after an unsuccessful flag evaluation
- `finally`, unconditionally after flag evaluation
```mermaid
flowchart LR
B(('Before' stage)) ---> FE[Flag Evaluation]
B -..->|Error| E
FE ---> A(('After' stage))
FE -..->|Error| E(('Error' stage))
A ---> F(('Finally' stage))
E -..-> F
```
Hooks can be configured to run globally (impacting all flag evaluations), per client, or per flag evaluation invocation. Some example use cases for a hook include adding additional data to the [evaluation context](./03-evaluation-context.md), performing validation on the received flag value, providing data to telemetric tools, and logging errors.
### Definitions
**Hook**: Application author/integrator-supplied logic that is called by the OpenFeature framework at a specific stage.
**Stage**: An explicit portion of the flag evaluation lifecycle. e.g. `before` being "before the [resolution](../glossary.md#resolving-flag-values) is run.
**Invocation**: A single call to evaluate a flag. `client.getBooleanValue(..)` is an invocation.
**API**: The global API singleton.
### 4.1. Hook context
Hook context exists to provide hooks with information about the invocation and propagate data between hook stages.
#### Requirement 4.1.1
> Hook context **MUST** provide: the `flag key`, `flag value type`, `evaluation context`, `default value`, and `hook data`.
The `evaluation context` provided in the hook context refers to the **merged evaluation context** as specified in [Requirement 3.2.3](./03-evaluation-context.md#requirement-323).
#### Requirement 4.1.2
> The `hook context` **SHOULD** provide access to the `client metadata` and the `provider metadata` fields.
#### Requirement 4.1.3
> The `flag key`, `flag type`, and `default value` properties **MUST** be immutable. If the language does not support immutability, the hook **MUST NOT** modify these properties.
#### Condition 4.1.4
> The implementation uses the dynamic-context paradigm.
see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm)
##### Conditional Requirement 4.1.4.1
> The evaluation context **MUST** be mutable only within the `before` hook.
#### Requirement 4.1.5
> The `hook data` **MUST** be mutable.
Either the `hook data` reference itself must be mutable, or it must allow mutation of its contents.
Mutable reference:
```js
hookContext.hookData = {'my-key': 'my-value'}
```
Mutable content:
```js
hookContext.hookData.set('my-key', 'my-value')
```
### 4.2. Hook Hints
#### Requirement 4.2.1
> `hook hints` **MUST** be a structure supports definition of arbitrary properties, with keys of type `string`, and values of type `boolean | string | number | datetime | structure`.
#### Condition 4.2.2
> The implementation language supports a mechanism for marking data as immutable.
##### Conditional Requirement 4.2.2.1
> Condition: `Hook hints` **MUST** be immutable.
##### Conditional Requirement 4.2.2.2
> Condition: The client `metadata` field in the `hook context` **MUST** be immutable.
##### Conditional Requirement 4.2.2.3
> Condition: The provider `metadata` field in the `hook context` **MUST** be immutable.
### 4.3. Hook creation and parameters
#### Requirement 4.3.1
> Hooks **MUST** specify at least one stage.
#### Requirement 4.3.2
> `Hook data` **MUST** must be created before the first `stage` invoked in a hook for a specific evaluation and propagated between each `stage` of the hook. The hook data is not shared between different hooks.
Example showing data between `before` and `after` stage for two different hooks.
```mermaid
sequenceDiagram
actor Application
participant Client
participant HookA
participant HookB
Application->>Client: getBooleanValue('my-bool', myContext, false)
activate Client
Client-->>Client: create hook data for HookA
Client->>HookA: before(hookContext: {data: {}, ... })
activate HookA
HookA-->>HookA: hookContext.hookData.set('key',' data for A')
HookA-->>Client: (return)
deactivate HookA
Client-->>Client: create hook data for HookB
Client->>HookB: before(hookContext: {data: {}, ... }, hints)
activate HookB
HookB-->>HookB: hookContext.hookData.set('key', 'data for B')
deactivate HookB
Client-->>Client: Flag evaluation
Client->>HookB: after(hookContext: {data: {key: 'data for B'}, ... }, detail, hints)
activate HookB
HookB-->>Client: (return)
deactivate HookB
Client->>HookA: after(hookContext: {data: {'key': 'data for A'}, ... })
activate HookA
HookA-->>Client: (return)
deactivate HookA
Client-->>Application: true
deactivate Client
```
#### Condition 4.3.2
> The implementation uses the dynamic-context paradigm.
see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm)
##### Conditional Requirement 4.3.2.1
> The `before` stage **MUST** run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters and returns either an `evaluation context` or nothing.
```java
EvaluationContext | void before(HookContext hookContext, HookHints hints);
```
#### Condition 4.3.3
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
> The implementation uses the static-context paradigm.
see: [static-context paradigm](../glossary.md#static-context-paradigm)
##### Conditional Requirement 4.3.3.1
> The `before` stage **MUST** run before flag resolution occurs. It accepts a `hook context` (required) and `hook hints` (optional) as parameters. It has no return value.
```java
void before(HookContext hookContext, HookHints hints);
```
#### Requirement 4.3.4
> Any `evaluation context` returned from a `before` hook **MUST** be passed to subsequent `before` hooks (via `HookContext`).
#### Requirement 4.3.5
> When `before` hooks have finished executing, any resulting `evaluation context` **MUST** be merged with the existing `evaluation context`.
Evaluation context merge order is defined in [Context levels and merging](./03-evaluation-context.md#32-context-levels-and-merging).
#### Requirement 4.3.6
> The `after` stage **MUST** run after flag resolution occurs. It accepts a `hook context` (required), `evaluation details` (required) and `hook hints` (optional). It has no return value.
#### Requirement 4.3.7
> The `error` hook **MUST** run when errors are encountered in the `before` stage, the `after` stage or during flag resolution. It accepts `hook context` (required), `exception` representing what went wrong (required), and `hook hints` (optional). It has no return value.
#### Requirement 4.3.8
> The `finally` hook **MUST** run after the `before`, `after`, and `error` stages. It accepts a `hook context` (required), `evaluation details` (required) and `hook hints` (optional). It has no return value.
The evaluation details passed to the `finally` stage matches the evaluation details returned to the application author.
#### Condition 4.3.9
> `finally` is a reserved word in the language.
##### Conditional Requirement 4.3.9.1
> Instead of `finally`, `finallyAfter` **SHOULD** be used.
### 4.4. Hook registration & ordering
#### Requirement 4.4.1
> The API, Client, Provider, and invocation **MUST** have a method for registering hooks.
```js
OpenFeature.addHooks(new Hook1());
//...
Client client = OpenFeature.getClient();
client.addHooks(new Hook2());
`
//...
client.getValue('my-flag', 'defaultValue', new Hook3());
```
#### Requirement 4.4.2
> Hooks **MUST** be executed "stack-wise" with respect to flag resolution, prioritizing increasing specificity (API, Client, Invocation, Provider) first, and the order in which they were added second.
Before flag resolution (the `before` stage), hooks run in the order `API` -> `Client` -> `Invocation` -> `Provider`, and within those, in the order in which they were added.
After flag evaluation (the `after`, `error`, or `finally` stages), hooks run in the order `Provider` -> `Invocation` -> `Client` -> `API`, and within those, in reverse of the order in which they were added.
This achieves intuitive "stack-like" or "LIFO" behavior for side effects and transformations.
Given hooks A - H, each implementing the both the `before` and `after` stages, added at the following levels and order:
- API: [A, B]
- Client: [C, D]
- Invocation: [E, F]
- Provider: [G, H]
The expected order of execution is:
```mermaid
flowchart BT
subgraph FlagResolution [Flag Resolution]
flagResolution[flagResolution]
end
subgraph Provider [Provider Layer]
G_before[G.before]
H_before[H.before]
G_after[G.after]
H_after[H.after]
end
subgraph Invocation [Invocation Layer]
E_before[E.before]
F_before[F.before]
E_after[E.after]
F_after[F.after]
end
subgraph Client [Client Layer]
C_before[C.before]
D_before[D.before]
C_after[C.after]
D_after[D.after]
end
subgraph API [API Layer]
A_before[A.before]
B_before[B.before]
A_after[A.after]
B_after[B.after]
end
A_before --> B_before
B_before --> C_before
C_before --> D_before
D_before --> E_before
E_before --> F_before
F_before --> G_before
G_before --> H_before
H_before --> flagResolution
flagResolution --> H_after
H_after --> G_after
G_after --> F_after
F_after --> E_after
E_after --> D_after
D_after --> C_after
C_after --> B_after
B_after --> A_after
```
#### Requirement 4.4.3
> If a `finally` hook abnormally terminates, evaluation **MUST** proceed, including the execution of any remaining `finally` hooks.
In languages with try/catch semantics, this means that exceptions thrown in `finally` hooks should be caught and not propagated up the call stack.
#### Requirement 4.4.4
> If an `error` hook abnormally terminates, evaluation **MUST** proceed, including the execution of any remaining `error` hooks.
In languages with try/catch semantics, this means that exceptions thrown in `error` hooks should be caught, and not propagated up the call stack.
#### Requirement 4.4.5
> If an error occurs in the `before` or `after` hooks, the `error` hooks **MUST** be invoked.
#### Requirement 4.4.6
> If an error occurs during the evaluation of `before` or `after` hooks, any remaining hooks in the `before` or `after` stages **MUST NOT** be invoked.
#### Requirement 4.4.7
> If an error occurs in the `before` hooks, the default value **MUST** be returned.
Before hooks can impact evaluation by various means, such as mutating the `evaluation context`. Therefore, an error in the `before` hooks is considered abnormal execution, and the default should be returned.
### [Flag evaluation options](../types.md#evaluation-options)
Usage might look something like:
```python
val = client.get_boolean_value('my-key', False, evaluation_options={
'hooks': new MyHook(),
'hook_hints': {'side-item': 'onion rings'}
})
```
see: [Flag evaluation options](./01-flag-evaluation.md#evaluation-options)
#### Requirement 4.5.1
> `Flag evaluation options` **MAY** contain `hook hints`, a map of data to be provided to hook invocations.
#### Requirement 4.5.2
> `hook hints` **MUST** be passed to each hook.
#### Requirement 4.5.3
> The hook **MUST NOT** alter the `hook hints` structure.
### 4.6. Hook data
Hook data exists to allow hook stages to share data for a specific evaluation. For instance a span
for OpenTelemetry could be created in a `before` stage and closed in an `after` stage.
Hook data is scoped to a specific hook instance. The different stages of a hook share the same data,
but different hooks have different hook data instances.
```Java
public Optional<EvaluationContext> before(HookContext context, HookHints hints) {
SpanBuilder builder = tracer.spanBuilder('sample')
.setParent(Context.current().with(Span.current()));
Span span = builder.startSpan()
context.hookData.set("span", span);
}
public void after(HookContext context, FlagEvaluationDetails details, HookHints hints) {
// Only accessible by this hook for this specific evaluation.
Object value = context.hookData.get("span");
if (value instanceof Span) {
Span span = (Span) value;
span.end();
}
}
```
#### Requirement 4.6.1
> `hook data` **MUST** be a structure supporting the definition of arbitrary properties, with keys of type `string`, and values of any type.
Access to hook data is restricted to only a single hook instance, and it has no serialization requirements, and as a result does not require any value type restrictions.
Example TypeScript definition:
```js
type HookData = Record<string, unknown>;
```

View File

@ -0,0 +1,249 @@
---
title: Events
description: Specification defining event semantics
toc_max_heading_level: 4
---
# 5. Events
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
## Overview
`Events` allow consumers (_application integrator_, _application author_, _integration author_) to react to state changes in the provider or underlying flag management system, such as flag definition changes, provider readiness, or error conditions. A provider may emit events or run a callback indicating that it received a certain event, optionally providing data associated with that event. Handlers registered on the `client` or the global `API` are then invoked with this data.
The data that providers supply in event payloads may include a list of `flag keys` changed, error messages, and possibly updated flag values.
```mermaid
graph
P(Provider) -->|emit event| A[API]
A -->|run handlers| AH("API (global) event handlers")
A --> C[Client]
C -->|run handlers| CH(Client event handlers)
```
The `domain` of a provider constitutes a logical scope for events.
Clients associated with a particular provider through a `domain` run event handlers only when that provider emits events, or one of its lifecycle functions terminates.
see: [domain](../glossary.md#domain)
### 5.1. Provider events
#### Requirement 5.1.1
> The `provider` **MAY** define a mechanism for signaling the occurrence of one of a set of events, including `PROVIDER_READY`, `PROVIDER_ERROR`, `PROVIDER_CONFIGURATION_CHANGED` and `PROVIDER_STALE`, with a `provider event details` payload.
Providers cannot emit `PROVIDER_CONTEXT_CHANGED` or `PROVIDER_RECONCILING` events.
These are emitted only by the SDK during context reconciliation.
If available, native event-emitter or observable/observer language constructs can be used.
When a provider is unable to evaluate flags (perhaps due to loss of connection with a remote service) the provider can signal this by emitting a `PROVIDER_ERROR` event.
When it recovers, it can emit a `PROVIDER_READY` event.
If the error state is irrecoverable, the `PROVIDER_FATAL` error code can be used.
If a provider caches rules-sets or previously evaluated flags, and such states cannot be considered up-to-date, the provider can signal this by emitting a `PROVIDER_STALE` event.
see: [provider event types](../types.md#provider-events), [`event details`](../types.md#provider-event-details), [events handlers and context reconciliation](#event-handlers-and-context-reconciliation)
#### Requirement 5.1.2
> When a `provider` signals the occurrence of a particular `event`, the associated `client` and `API` event handlers **MUST** run.
Client event handlers respect the dynamic binding of clients to providers via `domains`.
Client event handlers run when a lifecycle function terminates on the associated provider, or the associated provider emits an event.
see: [provider event types](./../types.md#provider-events) and [event handlers](#52-event-handlers).
#### Requirement 5.1.3
> When a `provider` signals the occurrence of a particular `event`, event handlers on clients which are not associated with that provider **MUST NOT** run.
Client event handlers respect the dynamic binding of clients to providers via `domains`.
Client event handlers do not run when a lifecycle function terminates on an unassociated provider, or an unassociated provider emits an event.
see [setting a provider](./01-flag-evaluation.md#setting-a-provider), [domain](../glossary.md#domain) for details.
#### Requirement 5.1.4
> `PROVIDER_ERROR` events **SHOULD** populate the `provider event details`'s `error message` field.
The error message field should contain an informative message as to the nature of the error.
See [event metadata](../types.md#provider-event-details)
#### Requirement 5.1.5
> `PROVIDER_ERROR` events **SHOULD** populate the `provider event details`'s `error code` field.
See [event metadata](../types.md#provider-event-details)
### 5.2. Event handlers
#### Requirement 5.2.1
> The `client` **MUST** provide a function for associating `handler functions` with a particular `provider event type`.
```java
// run the myClientOnReadyHandler function when the PROVIDER_READY event is fired
client.addHandler(ProviderEvents.Ready, myClientOnReadyHandler);
```
see: [provider events](#51-provider-events), [`provider event types`](../types.md#provider-events)
#### Requirement 5.2.2
> The `API` **MUST** provide a function for associating `handler functions` with a particular `provider event type`.
```java
// run the myGlobalErrorHandler function when the PROVIDER_READY event is fired
OpenFeature.addHandler(ProviderEvents.Error, myGlobalErrorHandler);
```
see: [provider events](#51-provider-events), [`provider event types`](../types.md#provider-events)
#### Requirement 5.2.3
> The `event details` **MUST** contain the `provider name` associated with the event.
The `provider name` indicates the provider from which the event originated.
This is especially relevant for global event handlers used for general monitoring, such as alerting on provider errors.
See [setting a provider](./01-flag-evaluation.md#setting-a-provider), [creating clients](./01-flag-evaluation.md#creating-clients).
#### Requirement 5.2.4
> The `handler function` **MUST** accept an `event details` parameter.
see: [`event details`](../types.md#event-details)
#### Requirement 5.2.5
> If a `handler function` terminates abnormally, other `handler functions` **MUST** run.
#### Requirement 5.2.6
> Event handlers **MUST** persist across `provider` changes.
If the underlying provider is changed, existing client and API event handlers will still fire.
This means that the order of provider configuration and event handler addition is independent.
#### Requirement 5.2.7
> The `API` and `client` **MUST** provide a function allowing the removal of event handlers.
```java
// remove an existing handler for a PROVIDER_CONFIGURATION_CHANGED event
client.removeHandler(ProviderEvents.ConfigurationChanged, myClientOnChangedHandler);
```
### Event handlers and initialization
Though providers themselves need not implement events, the `flag evaluation API` uses events to convey relevant state changes during configuration and initialization.
Implementations automatically emit `PROVIDER_READY` or `PROVIDER_ERROR` events depending on the outcome of the `initialize` function, if the provider has implemented one (if none is implemented, `PROVIDER_READY` runs unconditionally).
_Application authors_ and _application integrators_ use these events to wait for proper initialization of the provider and to do basic monitoring and error handling.
#### Requirement 5.3.1
> If the provider's `initialize` function terminates normally, `PROVIDER_READY` handlers **MUST** run.
See [provider initialization](./02-providers.md#24-initialization) and [setting a provider](./01-flag-evaluation.md#setting-a-provider).
#### Requirement 5.3.2
> If the provider's `initialize` function terminates abnormally, `PROVIDER_ERROR` handlers **MUST** run.
A failed initialization could represent an unrecoverable error, such as bad credentials or a missing file.
If a failed initialization could also represent a transient error.
A provider which maintains a persistent connection to a remote `flag management system` may attempt to reconnect, and emit `PROVIDER_READY` after a failed initialization.
See [provider initialization](./02-providers.md#24-initialization) and [setting a provider](./01-flag-evaluation.md#setting-a-provider).
#### Requirement 5.3.3
> Handlers attached after the provider is already in the associated state, **MUST** run immediately.
Handlers may be attached at any point in the application lifecycle.
Handlers should run immediately if the provider is already in the associated state.
For instance, _application authors_ may attach readiness handlers to be confident that the system is ready to evaluate flags.
If such handlers are attached after the provider underlying the client has already been initialized, they should run immediately.
See [provider initialization](./02-providers.md#24-initialization), [setting a provider](./01-flag-evaluation.md#setting-a-provider).
### Event handlers and context reconciliation
Providers built to conform to the static context paradigm feature two additional events: `PROVIDER_RECONCILING` and `PROVIDER_CONTEXT_CHANGED`.
When the provider is reconciling its internal state (the `on context changed` function is running and not yet terminated), the SDK emits `PROVIDER_RECONCILING` and transitions the provider into state `RECONCILING`.
This can be particularly useful for displaying loading indicators while the [evaluation context](./03-evaluation-context.md) is being reconciled.
If the `on context changed` function terminates normally, the SDK emits (`PROVIDER_CONTEXT_CHANGED`) and transitions the provider into the `READY` state, otherwise it emits `PROVIDER_ERROR` and transitions the provider into `ERROR` state.
The `PROVIDER_CONTEXT_CHANGED` is used to signal that the associated context has been changed, and flags should be re-evaluated.
This can be particularly useful for triggering UI repaints in multiple components when one component updates the [evaluation context](./03-evaluation-context.md).
```mermaid
---
title: Provider context reconciliation
---
stateDiagram-v2
direction TB
READY --> READY:emit(PROVIDER_CONTEXT_CHANGED)*
READY --> RECONCILING:emit(PROVIDER_RECONCILING)
RECONCILING --> READY:emit(PROVIDER_CONTEXT_CHANGED)
RECONCILING --> ERROR:emit(PROVIDER_ERROR)
```
\* Implementations may allow for providers to reconcile synchronously, in which case no `PROVIDER_RECONCILING` event is emitted.
#### Condition 5.3.4
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
> The implementation uses the static-context paradigm.
see: [static-context paradigm](../glossary.md#static-context-paradigm)
##### Conditional Requirement 5.3.4.1
> While the provider's `on context changed` function is executing, associated `RECONCILING` handlers **MUST** run.
The implementation must run any `RECONCILING` handlers associated with the provider while the provider is reconciling its state.
In languages with asynchronous semantics, the emission of this event can be skipped if the `on context changed` function of the provider in question executes synchronously for a given provider, no other operations can take place while it runs.
see: [provider event types](../types.md#provider-events), [provider events](#51-provider-events), [provider context reconciliation](02-providers.md#26-provider-context-reconciliation)
##### Conditional Requirement 5.3.4.2
> If the provider's `on context changed` function terminates normally, and no other invocations have yet to terminate, associated `PROVIDER_CONTEXT_CHANGED` handlers **MUST** run.
The implementation must run any `PROVIDER_CONTEXT_CHANGED` handlers associated with the provider after the provider has reconciled its state and returned from the `on context changed` function.
The `PROVIDER_CONTEXT_CHANGED` is not emitted from the provider itself; the SDK implementation must run the `PROVIDER_CONTEXT_CHANGED` handlers if the `on context changed` function terminates normally.
It's possible that the `on context changed` function is invoked simultaneously or in quick succession; in this case the SDK will only run the `PROVIDER_CONTEXT_CHANGED` handlers after all reentrant invocations have terminated, and the last to terminate was successful (terminated normally).
see: [provider event types](../types.md#provider-events), [provider events](#51-provider-events), [provider context reconciliation](02-providers.md#26-provider-context-reconciliation)
##### Conditional Requirement 5.3.4.3
> If the provider's `on context changed` function terminates abnormally, and no other invocations have yet to terminate, associated `PROVIDER_ERROR` handlers **MUST** run.
The `PROVIDER_ERROR` is not emitted from the provider itself; the SDK implementation must run the `PROVIDER_ERROR` handlers if the `on context changed` throws or otherwise signals an error.
It's possible that the `on context changed` function is invoked simultaneously or in quick succession; in this case the SDK will only run the `PROVIDER_ERROR` handlers after all reentrant invocations have terminated, and the last to terminate was unsuccessful (terminated abnormally).
see: [provider event types](../types.md#provider-events), [provider events](#51-provider-events), [provider context reconciliation](02-providers.md#26-provider-context-reconciliation)
#### Requirement 5.3.5
> If the provider emits an event, the value of the client's `provider status` **MUST** be updated accordingly.
Some providers may emit events spontaneously, based on changes in their internal state (connections, caches, etc).
The SDK must update its internal representation of the provider's state accordingly:
| Event | Associated Status |
| -------------------------------- | ------------------------------------------------------- |
| `PROVIDER_READY` | `READY` |
| `PROVIDER_STALE` | `STALE` |
| `PROVIDER_ERROR` | `ERROR`/`FATAL`* |
| `PROVIDER_CONFIGURATION_CHANGED` | N/A (provider remains in its current state) |
| `PROVIDER_CONTEXT_CHANGED` | N/A (only emitted by SDK during context reconciliation) |
| `PROVIDER_RECONCILING` | N/A (only emitted by SDK during context reconciliation) |
\* If the `error code` associated with the error indicates `PROVIDER_FATAL`, the state is set to `FATAL`
see: [provider lifecycle management](01-flag-evaluation.md#17-provider-lifecycle-management), [provider status](../types.md#provider-status) [error codes](../types.md#error-code)

View File

@ -0,0 +1,107 @@
---
title: Tracking
description: Specification defining tracking API
toc_max_heading_level: 4
---
# 6. Tracking
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
## Overview
The `tracking API` enables the association of feature flag evaluations with subsequent actions or application states, in order to facilitate experimentation and analysis of the impact of feature flags on business objectives.
Combined with hooks which report feature flag evaluations to the analytics platform in question, tracking can allow for robust experimentation even for flag management systems that don't support tracking directly.
```mermaid
sequenceDiagram
Evaluation API->>+Tracking Hook: evaluate
Tracking Hook->>Analytics Platform: before
Tracking Hook->>Analytics Platform: after
Tracking Hook->>-Evaluation API: evaluate
Evaluation API->>Analytics Platform: track
```
### 6.1. Tracking API
#### Condition 6.1.1
> The implementation uses the dynamic-context paradigm.
see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm)
##### Conditional Requirement 6.1.1.1
> The `client` **MUST** define a function for tracking the occurrence of a particular action or application state, with parameters `tracking event name` (string, required), `evaluation context` (optional) and `tracking event details` (optional), which returns nothing.
```java
// example tracking event recording that a subject reached a page associated with a business goal
client.track("visited-promo-page", evaluationContext);
// example tracking event recording that a subject performed an action associated with a business goal, with the tracking event details having a particular numeric value
client.track("clicked-checkout", evaluationContext, new TrackingEventDetails(99.77));
// example tracking event recording that a subject performed an action associated with a business goal, with the tracking event details having a particular numeric value
client.track("clicked-checkout", evaluationContext, new TrackingEventDetails(99.77).add("currencyCode", "USD"));
```
See [evaluation context](../types.md#evaluation-context), [tracking event details](#62-tracking-event-details).
#### Condition 6.1.2
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
> The implementation uses the static-context paradigm.
see: [static-context paradigm](../glossary.md#static-context-paradigm)
##### Conditional Requirement 6.1.2.1
> The `client` **MUST** define a function for tracking the occurrence of a particular action or application state, with parameters `tracking event name` (string, required) and `tracking event details` (optional), which returns nothing.
The track function is a void function (function returning nothing).
Though it may be associated with network activity or other I/O, it need not be awaited by application authors.
```java
// example tracking event recording that a subject reached a page associated with a business goal
client.track("visited-promo-page");
// example tracking event recording that a subject performed an action associated with a business goal, with the tracking event details having a particular numeric value
client.track("clicked-checkout", new TrackingEventDetails(99.77));
// example tracking event recording that a subject performed an action associated with a business goal, with the tracking event details having a particular numeric and some additional details
client.track("clicked-checkout", new TrackingEventDetails(99.77).add("currencyCode", "USD"));
```
#### Requirement 6.1.3
> The evaluation context passed to the provider's track function **MUST** be merged in the order: API (global; lowest precedence) -> transaction -> client -> invocation (highest precedence), with duplicate values being overwritten.
The SDK passes a merged evaluation context to the provider's track function similarly to the manner it does in resolvers.
See: [context levels and merging](./03-evaluation-context.md#32-context-levels-and-merging).
#### Requirement 6.1.4
> If the client's `track` function is called and the associated provider does not implement tracking, the client's `track` function **MUST** no-op.
### 6.2. Tracking Event Details
The `tracking event details` structure defines optional data pertinent to a particular `tracking event`.
#### Requirement 6.2.1
> The `tracking event details` structure **MUST** define an optional numeric `value`, associating a scalar quality with an `tracking event`.
`Value` is a well-defined field which some providers may map to equivalent numeric values in their API.
See [provider tracking support](./02-providers.md#27-tracking-support).
#### Requirement 6.2.2
> The `tracking event details` **MUST** support the inclusion of custom fields, having keys of type `string`, and values of type `boolean | string | number | structure`.
The `tracking event details` supports the addition of arbitrary fields, including nested objects, similar to the `evaluation context` and object-typed flag values.
See [structure](../types.md#structure), [evaluation context](.//03-evaluation-context.md).

View File

@ -0,0 +1,9 @@
{
"position": 3,
"label": "Sections",
"collapsible": false,
"link": {
"type": "generated-index",
"title": "Specification Sections"
}
}

View File

@ -1,3 +1,9 @@
---
title: Types and Data Structures
description: A description of types and data structures used within the OpenFeature specification.
sidebar_position: 2
---
# Types and Data Structures
## Overview
@ -22,31 +28,171 @@ Structured data, presented however is idiomatic in the implementation language,
### Datetime
A language primitive for representing a date and time, including timezone information.
A language primitive for representing a date and time, optionally including timezone information. If no timezone is specified, the date and time will be treated as UTC.
### Evaluation Details
A structure representing the result of the [flag evaluation process](./glossary.md#evaluating-flag-values), and made available in the [detailed flag resolution functions](./flag-evaluation.md#detailed-flag-evaluation), containing the following fields:
A structure representing the result of the [flag evaluation process](./glossary.md#evaluating-flag-values), and made available in the [detailed flag resolution functions](./sections/01-flag-evaluation.md#14-detailed-flag-evaluation), containing the following fields:
- flag key (string, required)
- value (boolean | string | number | structure, required)
- error code (string, optional)
- error code ([error code](#error-code), optional)
- error message (string, optional)
- reason (string, optional)
- variant (string, optional)
- flag metadata ([flag metadata](#flag-metadata))
> [!NOTE]
> Some type systems feature useful constraints that can enhance the ergonomics of the `evaluation details` structure.
> For example, in the case of an unsuccessful evaluation, `error code`, `reason`, and `error message` will be set, and `variant` will not.
> If the type system of the implementation supports the expression of such constraints, consider defining them.
### Resolution Details
A structure which contains a subset of the fields defined in the `evaluation details`, representing the result of the provider's [flag resolution process](./glossary.md#resolving-flag-values), including:
- value (boolean | string | number | structure, required)
- error code (string, optional)
- error code ([error code](#error-code), optional)
- error message (string, optional)
- reason (string, optional)
- variant (string, optional)
- flag metadata ([flag metadata](#flag-metadata), optional)
\*NOTE: The `resolution details` structure is not exposed to the Application Author. It defines the data which Provider Authors must return when resolving the value of flags.
#### Resolution Reason
A set of pre-defined reasons is enumerated below:
| Reason | Explanation |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| STATIC | The resolved value is static (no dynamic evaluation). |
| DEFAULT | The resolved value fell back to a pre-configured value (no dynamic evaluation occurred or dynamic evaluation yielded no result). |
| TARGETING_MATCH | The resolved value was the result of a dynamic evaluation, such as a rule or specific user-targeting. |
| SPLIT | The resolved value was the result of pseudorandom assignment. |
| CACHED | The resolved value was retrieved from a cache. |
| DISABLED | The resolved value was the result of the flag being disabled in the management system. |
| UNKNOWN | The reason for the resolved value could not be determined. |
| STALE | The resolved value is non-authoritative or possibly out of date |
| ERROR | The resolved value was the result of an error. |
> [!NOTE]
> The `reason` should not be limited to the reasons enumerated above. It can be any of the pre-defined reasons, or
> any string value. Some type systems have features which can increase the ergonomics of `reason`, for instance a union
> of pre-defined types with a string, or a rust-style enumeration which allows for enumerated values to have associated
> content.
```rust
enum Reason {
Static,
Default,
TargetingMatch,
Split,
Cached,
Unknown,
Stale,
Error,
Other(String)
}
let myReason = Reason::Other("my-reason".to_string());
```
> [!NOTE]
> The `resolution details` structure is not exposed to the Application Author.
> It defines the data which Provider Authors must return when resolving the value of flags.
### Error Code
An enumerated error code represented idiomatically in the implementation language.
| Error Code | Explanation |
| --------------------- | ------------------------------------------------------------------------------------------- |
| PROVIDER_NOT_READY | The value was resolved before the provider was initialized. |
| FLAG_NOT_FOUND | The flag could not be found. |
| PARSE_ERROR | An error was encountered parsing data, such as a flag configuration. |
| TYPE_MISMATCH | The type of the flag value does not match the expected type. |
| TARGETING_KEY_MISSING | The provider requires a targeting key and one was not provided in the `evaluation context`. |
| INVALID_CONTEXT | The `evaluation context` does not meet provider requirements. |
| PROVIDER_FATAL | The provider has entered an irrecoverable error state. |
| GENERAL | The error was for a reason not enumerated above. |
### Evaluation Context
See [evaluation context](./sections/03-evaluation-context.md).
### Evaluation Options
A structure containing the following fields:
- hooks (one or more [hooks](./hooks.md), optional)
- hooks (one or more [hooks](./sections/04-hooks.md), optional)
- hook hints ([hook hints structure](./sections/04-hooks.md#42-hook-hints), optional)
### Flag Metadata
A structure which supports definition of arbitrary properties, with keys of type `string`, and values of type `boolean`, `string`, or `number`.
This structure is populated by a provider for use by an [Application Author](./glossary.md#application-author) via the [Evaluation API](./glossary.md#evaluation-api) or an [Application Integrator](./glossary.md#application-integrator) via [hooks](./sections/04-hooks.md).
### Provider Status
An enumeration of possible provider states.
| Status | Explanation |
| ------------ | -------------------------------------------------------------------------------------------------- |
| NOT_READY | The provider has not been initialized. |
| READY | The provider has been initialized, and is able to reliably resolve flag values. |
| ERROR | The provider is initialized but is not able to reliably resolve flag values. |
| STALE | The provider's cached state is no longer valid and may not be up-to-date with the source of truth. |
| FATAL | The provider has entered an irrecoverable error state. |
| RECONCILING* | The provider is reconciling its state with a context change. |
\* [static context (client-side) paradigm](./glossary.md#static-context-paradigm) only
### Provider Event Details
A structure defining a provider event payload, including:
- flags changed (string[], optional)
- message (string, optional)
- error code ([error code](#error-code), optional)
- event metadata ([event metadata](#event-metadata))
### Event Details
A structure defining an event payload, including:
- provider name (string, required)
- flags changed (string[], optional)
- message (string, optional)
- error code ([error code](#error-code), optional)
- event metadata ([event metadata](#event-metadata))
### Event Metadata
A structure supporting the addition of arbitrary event data.
It supports definition of arbitrary properties, with keys of type `string`, and values of type `boolean`, `string`, or `number`.
### Provider Events
An enumeration of provider events.
| Event | Explanation |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------- |
| PROVIDER_READY | The provider is ready to perform flag evaluations. |
| PROVIDER_ERROR | The provider signaled an error. |
| PROVIDER_CONFIGURATION_CHANGED | A change was made to the backend flag configuration. |
| PROVIDER_STALE | The provider's cached state is no longer valid and may not be up-to-date with the source of truth. |
| PROVIDER_RECONCILING* | The context associated with the provider has changed, and the provider has not yet reconciled its associated state. |
| PROVIDER_CONTEXT_CHANGED* | The context associated with the provider has changed, and the provider has reconciled its associated state. |
\* [static context (client-side) paradigm](./glossary.md#static-context-paradigm) only
### Handler Functions
A function or method which can be associated with a `provider event`, and runs when that event occurs.
It declares an `event details` parameter.
### Tracking Event Details
A structure which supports definition of arbitrary properties, including nested objects, similar to the `evaluation context` and object-typed flag values.
See [tracking event details](./sections/06-tracking.md#62-tracking-event-details), [evaluation context](#evaluation-context).

View File

@ -0,0 +1,8 @@
FROM cgr.dev/chainguard/python:latest
WORKDIR .
COPY ./spec_finder.py ./
VOLUME /appdir
ENTRYPOINT ["python", "spec_finder.py"]

View File

@ -0,0 +1,49 @@
# Repo Parser
This will parse the contents of an OpenFeature repo and determine how they adhere to the spec. This can be gamed and assumes everyone is participating in good faith.
We look for a `.specrc` file in the root of a repository to figure out how to find test cases that are annotated with the spec number and the text of the spec. We can then produce a report which says "you're covered" or details about how you're not covered. The goal of this is to use that resulting report to power a spec-compliance matrix for end users to vet SDK quality.
## Usage
```
$ docker build -t specfinder .
$ docker run --mount src=/path/tojava-sdk/,target=/appdir,type=bind -it specfinder \
spec_finder.py --code-directory /appdir --diff --json-report
```
### `.specrc`
This should be at the root of the repository.
`multiline_regex` captures the text which contains the test marker. In java, for instance, it's a specially crafted annotation. `number_subregex` and `text_subregex` which will match the substring found in the `multiline_regex` to parse the spec number and text found. These are multi-line regexes.
Example:
```conf
[spec]
file_extension=java
multiline_regex=@Specification\((?P<innards>.*?)\)\s*$
number_subregex=number\s*=\s*['"](.*?)['"]
text_subregex=text\s*=\s*['"](.*)['"]
```
You can test the regex in python like this to validate they work:
```
$ python3
Python 3.9.6 (default, Feb 3 2024, 15:58:27)
[Clang 15.0.0 (clang-1500.3.9.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import re
>>> text = ''' @Specification(number="4.3.6", text="The after stage MUST run after flag resolution occurs. It accepts a hook context (required), flag evaluation details (required) and hook hints (optional). It has no return value.")
... @Specification(number="4.3.7", text="The error hook MUST run when errors are encountered in the before stage, the after stage or during flag resolution. It accepts hook context (required), exception representing what went wrong (required), and hook hints (optional). It has no return value.")
... '''
>>> entries = re.findall(r'@Specification\((?P<innards>.*?)\)\s*$', text, re.MULTILINE | re.DOTALL)
>>> entries
['number="4.3.7", text="The error hook MUST run when errors are encountered in the before stage, the after stage or during flag resolution. It accepts hook context (required), exception representing what went wrong (required), and hook hints (optional). It has no return value."']
>>> re.findall(r'''number\s*=\s*['"](.*?)['"]''', entries[0], re.MULTILINE | re.DOTALL)
['4.3.7']
>>> re.findall(r'''text\s*=\s*['"](.*)['"]''', entries[0], re.MULTILINE | re.DOTALL)
['The error hook MUST run when errors are encountered in the before stage, the after stage or during flag resolution. It accepts hook context (required), exception representing what went wrong (required), and hook hints (optional). It has no return value.']
```

View File

@ -0,0 +1,17 @@
[tool.mypy]
files = "spec_finder.py"
local_partial_types = true # will become the new default from version 2
pretty = true
strict = true
[tool.ruff]
line-length = 120
target-version = "py312"
[tool.ruff.lint]
select = [
"I",
]
[tool.ruff.format]
quote-style = "single"

View File

@ -0,0 +1,4 @@
mypy
pytest
pytest-cov
ruff

215
tools/repo_parser/spec_finder.py Executable file
View File

@ -0,0 +1,215 @@
#!/usr/bin/python
from __future__ import annotations
import configparser
import json
import os
import re
import sys
import urllib.request
from typing import Any, TypedDict, cast
class Config(TypedDict):
file_extension: str
multiline_regex: str
number_subregex: str
text_subregex: str
inline_comment_prefix: str | None
Report = TypedDict(
'Report',
{
'extra': list[str],
'missing': list[str],
'different-text': list[str],
'good': list[str],
},
)
def _demarkdown(t: str) -> str:
return t.replace('**', '').replace('`', '').replace('"', '')
def get_spec_parser(code_dir: str) -> Config:
with open(os.path.join(code_dir, '.specrc')) as f:
data = '\n'.join(f.readlines())
typical = configparser.ConfigParser(comment_prefixes=())
typical.read_string(data)
retval = typical['spec']
if 'inline_comment_prefix' in retval:
# If an `inline_comment_prefix` is set, then we're using the inline
# comment approach, which should obviate artisnal regexes.
retval['multiline_regex'] = r'spec:(.*?):end'
retval['number_subregex'] = r'(?P<number>[\d.]+):'
retval['text_subregex'] = r'[\d.]+:(.*)'
else:
assert 'file_extension' in retval
assert 'multiline_regex' in retval
assert 'number_subregex' in retval
assert 'text_subregex' in retval
return cast(Config, retval)
def get_spec(force_refresh: bool = False, path_prefix: str = './') -> dict[str, Any]:
spec_path = os.path.join(path_prefix, 'specification.json')
print('Going to look in ', spec_path)
if os.path.exists(spec_path) and not force_refresh:
with open(spec_path) as f:
data = ''.join(f.readlines())
else:
# TODO: Status code check
spec_response = urllib.request.urlopen(
'https://raw.githubusercontent.com/open-feature/spec/main/specification.json'
)
raw = []
for i in spec_response.readlines():
raw.append(i.decode('utf-8'))
data = ''.join(raw)
with open(spec_path, 'w') as f:
f.write(data)
return cast('dict[str, Any]', json.loads(data))
def specmap_from_file(actual_spec: dict[str, Any]) -> dict[str, str]:
spec_map = {}
for entry in actual_spec['rules']:
if 'requirement' in entry['machine_id']:
if number := re.search(r'[\d.]+', entry['id']):
spec_map[number.group()] = _demarkdown(entry['content'])
else:
print(f'Skipping invalid ID {entry["id"]}')
if entry['children']:
for ch in entry['children']:
if 'requirement' in ch['machine_id']:
if number := re.search(r'[\d.]+', ch['id']):
spec_map[number.group()] = _demarkdown(ch['content'])
else:
print(f'Skipping invalid child ID {ch["id"]}')
return spec_map
def find_covered_specs(config: Config, data: str) -> dict[str, str]:
repo_specs = {}
for match in re.findall(config['multiline_regex'], data, re.MULTILINE | re.DOTALL):
match = match.replace('\n', '').replace(config['inline_comment_prefix'], '')
# normalize whitespace
match = re.sub(' {2,}', ' ', match.strip())
number = re.findall(config['number_subregex'], match)[0]
text_with_concat_chars = re.findall(config['text_subregex'], match, re.MULTILINE | re.DOTALL)
try:
text = ''.join(text_with_concat_chars).strip()
# We have to match for ") to capture text with parens inside, so we add the trailing " back in.
text = _demarkdown(eval('"%s"' % text))
repo_specs[number] = text
except Exception as e:
print(f"Skipping {match} b/c we couldn't parse it")
return repo_specs
def gen_report(from_spec: dict[str, str], from_repo: dict[str, str]) -> Report:
extra = set()
different_text = set()
good = set()
missing = set(from_spec.keys()) # assume they're all missing
for number, text in from_repo.items():
if number in missing:
missing.remove(number)
if number not in from_spec:
extra.add(number)
continue
if text == from_spec[number]:
good.add(number)
else:
different_text.add(number)
return {
'extra': sorted(extra),
'missing': sorted(missing),
'different-text': sorted(different_text),
'good': sorted(good),
}
def main(
code_directory: str,
refresh_spec: bool = False,
diff_output: bool = False,
limit_numbers: str | None = None,
json_report: bool = False,
) -> None:
actual_spec = get_spec(refresh_spec, path_prefix=code_directory)
config = get_spec_parser(code_directory)
spec_map = specmap_from_file(actual_spec)
repo_specs: dict[str, str] = {}
bad_num = 0
for root, dirs, files in os.walk('.', topdown=False):
for name in files:
F = os.path.join(root, name)
if ('.%s' % config['file_extension']) not in name:
continue
with open(F) as f:
data = ''.join(f.readlines())
repo_specs |= find_covered_specs(config, data)
report = gen_report(from_spec=spec_map, from_repo=repo_specs)
for number in report['different-text']:
bad_num += 1
print(f'{number} is bad.')
if diff_output:
print('Official:')
print('\t%s' % spec_map[number])
print('')
print('Ours:')
print('\t%s' % repo_specs[number])
bad_num += len(report['extra'])
for number in report['extra']:
print(f"{number} is defined in our tests, but couldn't find it in the spec")
missing = report['missing']
bad_num += len(missing)
if missing:
print('In the spec, but not in our tests: ')
for m in sorted(missing):
print(f' {m}: {spec_map[m]}')
if json_report:
report_txt = json.dumps(report, indent=4)
loc = os.path.join(code_directory, '%s-report.json' % config['file_extension'])
with open(loc, 'w') as f:
f.write(report_txt)
sys.exit(bad_num)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(description='Parse the spec to make sure our tests cover it')
parser.add_argument('--refresh-spec', action='store_true', help='Re-downloads the spec')
parser.add_argument('--diff-output', action='store_true', help='print the text differences')
parser.add_argument('--code-directory', action='store', required=True, help='directory to find code in')
parser.add_argument('--json-report', action='store_true', help='Store a json report into ${extension}-report.json')
parser.add_argument('specific_numbers', metavar='num', type=str, nargs='*', help='limit this to specific numbers')
args = parser.parse_args()
main(
code_directory=args.code_directory,
refresh_spec=args.refresh_spec,
diff_output=args.diff_output,
limit_numbers=args.specific_numbers,
json_report=args.json_report,
)

View File

@ -0,0 +1,128 @@
from spec_finder import Config, find_covered_specs, gen_report, specmap_from_file
def test_simple_singleline():
text = """
// spec:4.3.6:The after stage MUST run after flag resolution occurs. It accepts a hook context (required), flag evaluation details (required) and hook hints (optional). It has no return value.:end
"""
cfg: Config = {
'file_extension': 'rust',
'multiline_regex': r'spec:(.*):end',
'number_subregex': r'(?P<number>[\d.]+):',
'text_subregex': r'[\d.]+:(.*)',
'inline_comment_prefix': '//',
}
output = find_covered_specs(cfg, text)
assert '4.3.6' in output
assert (
output['4.3.6']
== 'The after stage MUST run after flag resolution occurs. It accepts a hook context (required), flag evaluation details (required) and hook hints (optional). It has no return value.'
)
def test_multiline_comment():
text = """
// spec:4.3.7:The error hook MUST run when errors are encountered in the
// before stage, the after stage or during flag resolution. It accepts hook
// context (required), exception representing what went wrong (required), and
// hook hints (optional). It has no return value.:end
"""
cfg: Config = {
'file_extension': 'rust',
'multiline_regex': r'spec:(.*):end',
'number_subregex': r'(?P<number>[\d.]+):',
'text_subregex': r'[\d.]+:(.*)',
'inline_comment_prefix': '//',
}
output = find_covered_specs(cfg, text)
assert '4.3.7' in output
assert (
output['4.3.7']
== """The error hook MUST run when errors are encountered in the before stage, the after stage or during flag resolution. It accepts hook context (required), exception representing what went wrong (required), and hook hints (optional). It has no return value."""
)
def test_report():
spec = {
'1.2.3': 'good text',
'2.3.4': 'different text',
'3.4.5': 'missing',
}
repo = {
'1.2.3': 'good text',
'2.3.4': 'it is different',
'4.5.6': 'extra',
}
report = gen_report(spec, repo)
assert len(report['good']) == 1
assert len(report['different-text']) == 1
assert len(report['missing']) == 1
assert len(report['extra']) == 1
assert report['good'] == ['1.2.3']
assert report['different-text'] == ['2.3.4']
assert report['missing'] == ['3.4.5']
assert report['extra'] == ['4.5.6']
def test_report_with_found_spec():
spec = {
'4.3.6': 'good text',
}
text = """
// spec:4.3.6:good text:end
"""
cfg: Config = {
'file_extension': 'rust',
'multiline_regex': r'spec:(.*):end',
'number_subregex': r'(?P<number>[\d.]+):',
'text_subregex': r'[\d.]+:(.*)',
'inline_comment_prefix': '//',
}
output = find_covered_specs(cfg, text)
report = gen_report(spec, output)
assert report['good'] == ['4.3.6']
def test_specmap_from_file():
actual_spec = {
'rules': [
{
'id': 'Requirement 1.1.1',
'machine_id': 'requirement_1_1_1',
'content': 'The `API`, and any state it maintains SHOULD exist as a global singleton, even in cases wherein multiple versions of the `API` are present at runtime.',
'RFC 2119 keyword': 'SHOULD',
'children': [],
},
{
'id': 'Condition 2.2.2',
'machine_id': 'condition_2_2_2',
'content': 'The implementing language type system differentiates between strings, numbers, booleans and structures.',
'RFC 2119 keyword': None,
'children': [
{
'id': 'Conditional Requirement 2.2.2.1',
'machine_id': 'conditional_requirement_2_2_2_1',
'content': 'The `feature provider` interface MUST define methods for typed flag resolution, including boolean, numeric, string, and structure.',
'RFC 2119 keyword': 'MUST',
'children': [],
}
],
},
]
}
spec_map = specmap_from_file(actual_spec)
assert len(spec_map) == 2
assert (
spec_map['1.1.1']
== 'The API, and any state it maintains SHOULD exist as a global singleton, even in cases wherein multiple versions of the API are present at runtime.'
)
assert (
spec_map['2.2.2.1']
== 'The feature provider interface MUST define methods for typed flag resolution, including boolean, numeric, string, and structure.'
)

View File

@ -95,7 +95,7 @@ def find_rfc_2119_keyword(content):
) is not None:
return rfc_2119_keyword_regex
def parsed_content_to_heirarchy(parsed_content):
def parsed_content_to_hierarchy(parsed_content):
'Turns a bunch of headline & content pairings into a tree of requirements'
content_tree = []
headline_stack = []
@ -181,9 +181,9 @@ def content_tree_to_spec(ct):
def parse(markdown_file_path):
with open(markdown_file_path, "r") as markdown_file:
content_finder = re.compile(r'^(?P<level>#+)(?P<headline>[^\n]+)(?P<rest>[^#]*)', re.MULTILINE)
content_finder = re.compile(r'^(?P<level>####+)(?P<headline>[^\n]+)\n+?.*?\n+?(?P<rest>>\s[^#?]*)', re.MULTILINE)
parsed = content_finder.findall(markdown_file.read())
return parsed_content_to_heirarchy(parsed)
return parsed_content_to_hierarchy(parsed)
def write_json_specifications(requirements):
for md_absolute_file_path, requirement_sections in requirements.items():

File diff suppressed because one or more lines are too long

View File

@ -4,27 +4,27 @@
Some content.
##### Requirement Some requirement
#### Requirement Some requirement
> This **MUST** be done.
##### Requirement Some other requirement
#### Requirement Some other requirement
> This **MUST NOT** be done.
##### Requirement Another requirement-name
#### Requirement Another requirement-name
> This **SHOULD** be done in a certain way.
##### Condition 1
#### Condition 1
> This is a condition.
>
> ##### Condition 1.1
> #### Condition 1.1
>
> > This is a condition.
> >
> > ##### Condition 1.1.1
> > #### Condition 1.1.1
> >
> > > This is a condition.
> > >
@ -40,7 +40,7 @@ Some content.
> > >
> > > > This **MAY** be done.
>
> ##### Condition 1.2
> #### Condition 1.2
>
> > This is a condition.
> >
@ -50,11 +50,11 @@ Some content.
Some content.
##### Requirement This is the name of-the-requirement
#### Requirement This is the name of-the-requirement
> This **MUST NOT** be done.
##### Condition 2
#### Condition 2
> This is a condition.
>