@spotify-confidence/openfeature-server-provider-local 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +80 -58
- package/dist/confidence_resolver.wasm +0 -0
- package/dist/index.browser.d.ts +60 -26
- package/dist/index.browser.js +168 -759
- package/dist/index.node.d.ts +60 -26
- package/dist/index.node.js +168 -759
- package/dist/messages-Bw39oRlC.js +524 -0
- package/dist/messages-CvypvyG4.js +2 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.0](https://github.com/spotify/confidence-resolver/compare/openfeature-provider-js-v0.2.0...openfeature-provider-js-v0.3.0) (2025-12-02)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* migrate to cdn state fetch, publish logs using client secret ([#166](https://github.com/spotify/confidence-resolver/issues/166))
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* migrate to cdn state fetch, publish logs using client secret ([#166](https://github.com/spotify/confidence-resolver/issues/166)) ([6c8d959](https://github.com/spotify/confidence-resolver/commit/6c8d959f124faa419c1ace103d8832457248eb26))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Dependencies
|
|
16
|
+
|
|
17
|
+
* The following workspace dependencies were updated
|
|
18
|
+
* dependencies
|
|
19
|
+
* rust-guest bumped from 0.1.10 to 0.1.11
|
|
20
|
+
|
|
3
21
|
## [0.2.0](https://github.com/spotify/confidence-resolver/compare/openfeature-provider-js-v0.1.1...openfeature-provider-js-v0.2.0) (2025-11-24)
|
|
4
22
|
|
|
5
23
|
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Confidence OpenFeature Local Provider for JavaScript
|
|
2
2
|
|
|
3
3
|
OpenFeature provider for the Spotify Confidence resolver (local mode, powered by WebAssembly). It periodically fetches resolver state, evaluates flags locally, and flushes evaluation logs to the Confidence backend.
|
|
4
4
|
|
|
@@ -29,6 +29,18 @@ Notes:
|
|
|
29
29
|
|
|
30
30
|
---
|
|
31
31
|
|
|
32
|
+
## Getting Your Credentials
|
|
33
|
+
|
|
34
|
+
You'll need a **client secret** from Confidence to use this provider.
|
|
35
|
+
|
|
36
|
+
**📖 See the [Integration Guide: Getting Your Credentials](../INTEGRATION_GUIDE.md#getting-your-credentials)** for step-by-step instructions on:
|
|
37
|
+
- How to navigate the Confidence dashboard
|
|
38
|
+
- Creating a Backend integration
|
|
39
|
+
- Creating a test flag for verification
|
|
40
|
+
- Best practices for credential storage
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
32
44
|
## Quick start (Node)
|
|
33
45
|
|
|
34
46
|
```ts
|
|
@@ -37,8 +49,6 @@ import { createConfidenceServerProvider } from '@spotify-confidence/openfeature-
|
|
|
37
49
|
|
|
38
50
|
const provider = createConfidenceServerProvider({
|
|
39
51
|
flagClientSecret: process.env.CONFIDENCE_FLAG_CLIENT_SECRET!,
|
|
40
|
-
apiClientId: process.env.CONFIDENCE_API_CLIENT_ID!,
|
|
41
|
-
apiClientSecret: process.env.CONFIDENCE_API_CLIENT_SECRET!,
|
|
42
52
|
// initializeTimeout?: number
|
|
43
53
|
// flushInterval?: number
|
|
44
54
|
// fetch?: typeof fetch (Node <18 or custom transport)
|
|
@@ -49,13 +59,20 @@ await OpenFeature.setProviderAndWait(provider);
|
|
|
49
59
|
|
|
50
60
|
const client = OpenFeature.getClient();
|
|
51
61
|
|
|
62
|
+
// Create evaluation context with user attributes for targeting
|
|
63
|
+
const context = {
|
|
64
|
+
targetingKey: 'user-123',
|
|
65
|
+
country: 'US',
|
|
66
|
+
plan: 'premium',
|
|
67
|
+
};
|
|
68
|
+
|
|
52
69
|
// Evaluate a boolean flag
|
|
53
|
-
const
|
|
54
|
-
console.log(
|
|
70
|
+
const enabled = await client.getBooleanValue('test-flag.enabled', false, context);
|
|
71
|
+
console.log('Flag value:', enabled);
|
|
55
72
|
|
|
56
73
|
// Evaluate a nested value from an object flag using dot-path
|
|
57
74
|
// e.g. flag key "experiments" with payload { groupA: { ratio: 0.5 } }
|
|
58
|
-
const ratio = await client.getNumberValue('experiments.groupA.ratio', 0,
|
|
75
|
+
const ratio = await client.getNumberValue('experiments.groupA.ratio', 0, context);
|
|
59
76
|
|
|
60
77
|
// On shutdown, flush any pending logs
|
|
61
78
|
await provider.onClose();
|
|
@@ -63,61 +80,73 @@ await provider.onClose();
|
|
|
63
80
|
|
|
64
81
|
---
|
|
65
82
|
|
|
66
|
-
##
|
|
83
|
+
## Evaluation Context
|
|
67
84
|
|
|
68
|
-
|
|
69
|
-
- `apiClientId` (string, required): OAuth client ID for Confidence IAM.
|
|
70
|
-
- `apiClientSecret` (string, required): OAuth client secret for Confidence IAM.
|
|
71
|
-
- `initializeTimeout` (number, optional): Max ms to wait for initial state fetch. Defaults to 30_000.
|
|
72
|
-
- `flushInterval` (number, optional): Interval in ms for sending evaluation logs. Defaults to 10_000.
|
|
73
|
-
- `fetch` (optional): Custom `fetch` implementation. Required for Node < 18; for Node 18+ you can omit.
|
|
85
|
+
The evaluation context contains information about the user/session being evaluated for targeting and A/B testing.
|
|
74
86
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
87
|
+
### TypeScript/JavaScript Examples
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// Simple attributes
|
|
91
|
+
const context = {
|
|
92
|
+
targetingKey: 'user-123',
|
|
93
|
+
country: 'US',
|
|
94
|
+
plan: 'premium',
|
|
95
|
+
age: 25,
|
|
96
|
+
};
|
|
97
|
+
```
|
|
78
98
|
|
|
79
99
|
---
|
|
80
100
|
|
|
81
|
-
##
|
|
82
|
-
If you're currently using the ["online resolver"](https://github.com/spotify/confidence-sdk-js/tree/main/packages/openfeature-server-provider) and wants to improve the resolve latency, [migration](MIGRATION.md) is easy!
|
|
101
|
+
## Error Handling
|
|
83
102
|
|
|
84
|
-
|
|
103
|
+
The provider uses a **default value fallback** pattern - when evaluation fails, it returns your specified default value instead of throwing an error.
|
|
85
104
|
|
|
86
|
-
|
|
105
|
+
**📖 See the [Integration Guide: Error Handling](../INTEGRATION_GUIDE.md#error-handling)** for:
|
|
106
|
+
- Common failure scenarios
|
|
107
|
+
- Error codes and meanings
|
|
108
|
+
- Production best practices
|
|
109
|
+
- Monitoring recommendations
|
|
87
110
|
|
|
88
|
-
|
|
111
|
+
### TypeScript/JavaScript Examples
|
|
89
112
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
> **Coming soon**: We're developing support for custom materialization storage (Redis, database, etc.) that will eliminate this network call and keep evaluation latency consistently low.
|
|
113
|
+
```typescript
|
|
114
|
+
// The provider returns the default value on errors
|
|
115
|
+
const enabled = await client.getBooleanValue('my-flag.enabled', false, context);
|
|
116
|
+
// enabled will be 'false' if evaluation failed
|
|
95
117
|
|
|
96
|
-
|
|
118
|
+
// For detailed error information, use getBooleanDetails()
|
|
119
|
+
const details = await client.getBooleanDetails('my-flag.enabled', false, context);
|
|
120
|
+
if (details.errorCode) {
|
|
121
|
+
console.error('Flag evaluation error:', details.errorMessage);
|
|
122
|
+
console.log('Reason:', details.reason);
|
|
123
|
+
}
|
|
124
|
+
```
|
|
97
125
|
|
|
98
|
-
|
|
99
|
-
- The user's context attributes change (e.g., different country, device type)
|
|
100
|
-
- The flag's targeting rules are modified
|
|
101
|
-
- New assignments are paused
|
|
126
|
+
---
|
|
102
127
|
|
|
103
|
-
|
|
128
|
+
## Options
|
|
104
129
|
|
|
105
|
-
The
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
-
|
|
109
|
-
- No local storage or database setup required
|
|
130
|
+
- `flagClientSecret` (string, required): The flag client secret used during evaluation and authentication.
|
|
131
|
+
- `initializeTimeout` (number, optional): Max ms to wait for initial state fetch. Defaults to 30_000.
|
|
132
|
+
- `flushInterval` (number, optional): Interval in ms for sending evaluation logs. Defaults to 10_000.
|
|
133
|
+
- `fetch` (optional): Custom `fetch` implementation. Required for Node < 18; for Node 18+ you can omit.
|
|
110
134
|
|
|
111
|
-
|
|
135
|
+
The provider periodically:
|
|
136
|
+
- Refreshes resolver state (default every 30s)
|
|
137
|
+
- Flushes flag evaluation logs to the backend
|
|
112
138
|
|
|
113
|
-
|
|
114
|
-
- **Managed storage**: Confidence handles all storage, TTL, and consistency
|
|
115
|
-
- **Automatic renewal**: TTL is refreshed on each access
|
|
116
|
-
- **Global availability**: Materializations are available across all your services
|
|
139
|
+
---
|
|
117
140
|
|
|
118
|
-
|
|
141
|
+
## Sticky Assignments
|
|
142
|
+
|
|
143
|
+
The provider supports **Sticky Assignments** for consistent variant assignments across flag evaluations.
|
|
119
144
|
|
|
120
|
-
|
|
145
|
+
**📖 See the [Integration Guide: Sticky Assignments](../INTEGRATION_GUIDE.md#sticky-assignments)** for:
|
|
146
|
+
- How sticky assignments work
|
|
147
|
+
- Server-managed storage (zero configuration)
|
|
148
|
+
- Latency considerations
|
|
149
|
+
- Custom storage options (currently Java-only, coming soon to JavaScript)
|
|
121
150
|
|
|
122
151
|
---
|
|
123
152
|
|
|
@@ -129,13 +158,19 @@ Namespaces:
|
|
|
129
158
|
- Core: `cnfd:*`
|
|
130
159
|
- Fetch/middleware: `cnfd:fetch:*` (e.g. retries, auth renewals, request summaries)
|
|
131
160
|
|
|
161
|
+
Log levels are hierarchical:
|
|
162
|
+
- `cnfd:debug` enables debug, info, warn, and error
|
|
163
|
+
- `cnfd:info` enables info, warn, and error
|
|
164
|
+
- `cnfd:warn` enables warn and error
|
|
165
|
+
- `cnfd:error` enables error only
|
|
166
|
+
|
|
132
167
|
Enable logs:
|
|
133
168
|
|
|
134
169
|
- Node:
|
|
135
170
|
```bash
|
|
136
171
|
DEBUG=cnfd:* node app.js
|
|
137
172
|
# or narrower
|
|
138
|
-
DEBUG=cnfd:info,cnfd:
|
|
173
|
+
DEBUG=cnfd:info,cnfd:fetch:* node app.js
|
|
139
174
|
```
|
|
140
175
|
|
|
141
176
|
- Browser (in DevTools console):
|
|
@@ -171,19 +206,6 @@ The package exports a browser ESM build that compiles the WASM via streaming and
|
|
|
171
206
|
|
|
172
207
|
---
|
|
173
208
|
|
|
174
|
-
## Troubleshooting
|
|
175
|
-
|
|
176
|
-
- Provider stuck in NOT_READY/ERROR:
|
|
177
|
-
- Verify `apiClientId`/`apiClientSecret` and `flagClientSecret` are correct.
|
|
178
|
-
- Ensure outbound access to Confidence endpoints and GCS.
|
|
179
|
-
- Enable `DEBUG=cnfd:*` for more detail.
|
|
180
|
-
|
|
181
|
-
- No logs appear:
|
|
182
|
-
- Install `debug` and enable the appropriate namespaces.
|
|
183
|
-
- Check that your environment variable/`localStorage.debug` is set before your app initializes the provider.
|
|
184
|
-
|
|
185
|
-
---
|
|
186
|
-
|
|
187
209
|
## License
|
|
188
210
|
|
|
189
211
|
See the root `LICENSE`.
|
|
Binary file
|
package/dist/index.browser.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
|
2
2
|
import { EvaluationContext, JsonValue, Provider, ProviderMetadata, ProviderStatus, ResolutionDetails } from "@openfeature/server-sdk";
|
|
3
3
|
|
|
4
|
-
//#region src/proto/
|
|
4
|
+
//#region src/proto/confidence/flags/resolver/v1/types.d.ts
|
|
5
|
+
|
|
5
6
|
declare enum SdkId {
|
|
6
7
|
SDK_ID_UNSPECIFIED = 0,
|
|
7
8
|
SDK_ID_JAVA_PROVIDER = 1,
|
|
@@ -28,6 +29,33 @@ declare enum SdkId {
|
|
|
28
29
|
SDK_ID_JS_LOCAL_SERVER_PROVIDER = 22,
|
|
29
30
|
UNRECOGNIZED = -1,
|
|
30
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* (-- api-linter: core::0123::resource-annotation=disabled
|
|
34
|
+
* aip.dev/not-precedent: SDKs are not internal Confidence resources. --)
|
|
35
|
+
*/
|
|
36
|
+
interface Sdk {
|
|
37
|
+
/** Name of a Confidence SDKs. */
|
|
38
|
+
id?: SdkId | undefined;
|
|
39
|
+
/** Custom name for non-Confidence SDKs. */
|
|
40
|
+
customId?: string | undefined;
|
|
41
|
+
/** Version of the SDK. */
|
|
42
|
+
version: string;
|
|
43
|
+
}
|
|
44
|
+
declare const Sdk: MessageFns$2<Sdk>;
|
|
45
|
+
type Builtin$2 = Date | Function | Uint8Array | string | number | boolean | undefined;
|
|
46
|
+
type DeepPartial$2<T> = T extends Builtin$2 ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial$2<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial$2<U>> : T extends {} ? { [K in keyof T]?: DeepPartial$2<T[K]> } : Partial<T>;
|
|
47
|
+
type KeysOfUnion$2<T> = T extends T ? keyof T : never;
|
|
48
|
+
type Exact$2<P, I extends P> = P extends Builtin$2 ? P : P & { [K in keyof P]: Exact$2<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion$2<P>>]: never };
|
|
49
|
+
interface MessageFns$2<T> {
|
|
50
|
+
encode(message: T, writer?: BinaryWriter): BinaryWriter;
|
|
51
|
+
decode(input: BinaryReader | Uint8Array, length?: number): T;
|
|
52
|
+
fromJSON(object: any): T;
|
|
53
|
+
toJSON(message: T): unknown;
|
|
54
|
+
create<I extends Exact$2<DeepPartial$2<T>, I>>(base?: I): T;
|
|
55
|
+
fromPartial<I extends Exact$2<DeepPartial$2<T>, I>>(object: I): T;
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/proto/types.d.ts
|
|
31
59
|
declare enum ResolveReason {
|
|
32
60
|
/** RESOLVE_REASON_UNSPECIFIED - Unspecified enum. */
|
|
33
61
|
RESOLVE_REASON_UNSPECIFIED = 0,
|
|
@@ -50,11 +78,8 @@ declare enum ResolveReason {
|
|
|
50
78
|
RESOLVE_REASON_ERROR = 6,
|
|
51
79
|
UNRECOGNIZED = -1,
|
|
52
80
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
customId?: string | undefined;
|
|
56
|
-
version: string;
|
|
57
|
-
}
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/proto/resolver/api.d.ts
|
|
58
83
|
interface ResolveFlagsRequest {
|
|
59
84
|
/**
|
|
60
85
|
* If non-empty, the specific flags are resolved, otherwise all flags
|
|
@@ -113,10 +138,6 @@ interface ResolvedFlag {
|
|
|
113
138
|
/** The reason to why the flag could be resolved or not. */
|
|
114
139
|
reason: ResolveReason;
|
|
115
140
|
}
|
|
116
|
-
interface SetResolverStateRequest {
|
|
117
|
-
state: Uint8Array;
|
|
118
|
-
accountId: string;
|
|
119
|
-
}
|
|
120
141
|
/** Request for resolving flags with sticky (materialized) assignments */
|
|
121
142
|
interface ResolveWithStickyRequest {
|
|
122
143
|
/** The standard resolve request */
|
|
@@ -176,19 +197,36 @@ interface ResolveWithStickyResponse_MaterializationUpdate {
|
|
|
176
197
|
rule: string;
|
|
177
198
|
variant: string;
|
|
178
199
|
}
|
|
179
|
-
declare const
|
|
180
|
-
declare const
|
|
181
|
-
declare const
|
|
182
|
-
declare const
|
|
200
|
+
declare const ResolveFlagsRequest: MessageFns$1<ResolveFlagsRequest>;
|
|
201
|
+
declare const ResolveFlagsResponse: MessageFns$1<ResolveFlagsResponse>;
|
|
202
|
+
declare const ResolvedFlag: MessageFns$1<ResolvedFlag>;
|
|
203
|
+
declare const ResolveWithStickyRequest: MessageFns$1<ResolveWithStickyRequest>;
|
|
204
|
+
declare const MaterializationMap: MessageFns$1<MaterializationMap>;
|
|
205
|
+
declare const MaterializationInfo: MessageFns$1<MaterializationInfo>;
|
|
206
|
+
declare const ResolveWithStickyResponse: MessageFns$1<ResolveWithStickyResponse>;
|
|
207
|
+
declare const ResolveWithStickyResponse_Success: MessageFns$1<ResolveWithStickyResponse_Success>;
|
|
208
|
+
declare const ResolveWithStickyResponse_MissingMaterializations: MessageFns$1<ResolveWithStickyResponse_MissingMaterializations>;
|
|
209
|
+
declare const ResolveWithStickyResponse_MissingMaterializationItem: MessageFns$1<ResolveWithStickyResponse_MissingMaterializationItem>;
|
|
210
|
+
declare const ResolveWithStickyResponse_MaterializationUpdate: MessageFns$1<ResolveWithStickyResponse_MaterializationUpdate>;
|
|
211
|
+
type Builtin$1 = Date | Function | Uint8Array | string | number | boolean | undefined;
|
|
212
|
+
type DeepPartial$1<T> = T extends Builtin$1 ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial$1<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial$1<U>> : T extends {} ? { [K in keyof T]?: DeepPartial$1<T[K]> } : Partial<T>;
|
|
213
|
+
type KeysOfUnion$1<T> = T extends T ? keyof T : never;
|
|
214
|
+
type Exact$1<P, I extends P> = P extends Builtin$1 ? P : P & { [K in keyof P]: Exact$1<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion$1<P>>]: never };
|
|
215
|
+
interface MessageFns$1<T> {
|
|
216
|
+
encode(message: T, writer?: BinaryWriter): BinaryWriter;
|
|
217
|
+
decode(input: BinaryReader | Uint8Array, length?: number): T;
|
|
218
|
+
fromJSON(object: any): T;
|
|
219
|
+
toJSON(message: T): unknown;
|
|
220
|
+
create<I extends Exact$1<DeepPartial$1<T>, I>>(base?: I): T;
|
|
221
|
+
fromPartial<I extends Exact$1<DeepPartial$1<T>, I>>(object: I): T;
|
|
222
|
+
}
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region src/proto/messages.d.ts
|
|
225
|
+
interface SetResolverStateRequest {
|
|
226
|
+
state: Uint8Array;
|
|
227
|
+
accountId: string;
|
|
228
|
+
}
|
|
183
229
|
declare const SetResolverStateRequest: MessageFns<SetResolverStateRequest>;
|
|
184
|
-
declare const ResolveWithStickyRequest: MessageFns<ResolveWithStickyRequest>;
|
|
185
|
-
declare const MaterializationMap: MessageFns<MaterializationMap>;
|
|
186
|
-
declare const MaterializationInfo: MessageFns<MaterializationInfo>;
|
|
187
|
-
declare const ResolveWithStickyResponse: MessageFns<ResolveWithStickyResponse>;
|
|
188
|
-
declare const ResolveWithStickyResponse_Success: MessageFns<ResolveWithStickyResponse_Success>;
|
|
189
|
-
declare const ResolveWithStickyResponse_MissingMaterializations: MessageFns<ResolveWithStickyResponse_MissingMaterializations>;
|
|
190
|
-
declare const ResolveWithStickyResponse_MissingMaterializationItem: MessageFns<ResolveWithStickyResponse_MissingMaterializationItem>;
|
|
191
|
-
declare const ResolveWithStickyResponse_MaterializationUpdate: MessageFns<ResolveWithStickyResponse_MaterializationUpdate>;
|
|
192
230
|
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
|
|
193
231
|
type DeepPartial<T> = T extends Builtin ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> } : Partial<T>;
|
|
194
232
|
type KeysOfUnion<T> = T extends T ? keyof T : never;
|
|
@@ -212,8 +250,6 @@ interface LocalResolver {
|
|
|
212
250
|
//#region src/ConfidenceServerProviderLocal.d.ts
|
|
213
251
|
interface ProviderOptions {
|
|
214
252
|
flagClientSecret: string;
|
|
215
|
-
apiClientId: string;
|
|
216
|
-
apiClientSecret: string;
|
|
217
253
|
initializeTimeout?: number;
|
|
218
254
|
flushInterval?: number;
|
|
219
255
|
fetch?: typeof fetch;
|
|
@@ -250,8 +286,6 @@ declare class ConfidenceServerProviderLocal implements Provider {
|
|
|
250
286
|
private extractValue;
|
|
251
287
|
updateState(signal?: AbortSignal): Promise<void>;
|
|
252
288
|
flush(signal?: AbortSignal): Promise<void>;
|
|
253
|
-
private fetchResolveStateUri;
|
|
254
|
-
private fetchToken;
|
|
255
289
|
private static convertReason;
|
|
256
290
|
private static convertEvaluationContext;
|
|
257
291
|
/** Resolves with an evaluation of a Boolean flag */
|